mirror of
https://github.com/Tencent/tdesign-vue-next-starter.git
synced 2024-12-23 00:26:33 +08:00
Merge pull request #708 from Tencent/develop
This commit is contained in:
commit
ce285fa443
|
@ -1,4 +1,4 @@
|
|||
module.exports = {
|
||||
export default {
|
||||
// 一行最多 120 字符..
|
||||
printWidth: 120,
|
||||
// 使用 2 个空格缩进
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// commit-lint config
|
||||
module.exports = {
|
||||
export default {
|
||||
extends: ['@commitlint/config-conventional'],
|
||||
rules: {
|
||||
'type-enum': [
|
||||
|
|
70
package.json
70
package.json
|
@ -1,12 +1,14 @@
|
|||
{
|
||||
"name": "@tencent/tdesign-vue-next-starter",
|
||||
"version": "0.9.0",
|
||||
"version": "0.10.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev:mock": "vite --open --mode mock",
|
||||
"dev": "vite --open --mode development",
|
||||
"dev:linux": "vite --mode development",
|
||||
"build:test": "vite build --mode test",
|
||||
"build": "vue-tsc --noEmit && vite build --mode release",
|
||||
"build:type": "vue-tsc --noEmit",
|
||||
"build:site": "vue-tsc --noEmit && vite build --mode site",
|
||||
"preview": "vite preview",
|
||||
"lint": "eslint --ext .vue,.js,.jsx,.ts,.tsx ./ --max-warnings 0",
|
||||
|
@ -19,10 +21,10 @@
|
|||
"test:coverage": "echo \"no test:coverage specified,work in process\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@vueuse/core": "^10.6.1",
|
||||
"axios": "^1.6.2",
|
||||
"@vueuse/core": "^10.7.2",
|
||||
"axios": "^1.6.7",
|
||||
"dayjs": "^1.11.10",
|
||||
"echarts": "5.1.2",
|
||||
"echarts": "5.4.3",
|
||||
"lodash": "^4.17.21",
|
||||
"nprogress": "^0.2.0",
|
||||
"pinia": "^2.1.7",
|
||||
|
@ -30,50 +32,50 @@
|
|||
"qrcode.vue": "^3.4.1",
|
||||
"qs": "^6.11.2",
|
||||
"tdesign-icons-vue-next": "^0.2.2",
|
||||
"tdesign-vue-next": "^1.6.8",
|
||||
"tdesign-vue-next": "^1.9.3",
|
||||
"tvision-color": "^1.6.0",
|
||||
"vue": "~3.3.8",
|
||||
"vue-i18n": "^9.6.5",
|
||||
"vue-router": "~4.2.4"
|
||||
"vue": "~3.4.21",
|
||||
"vue-i18n": "^9.9.1",
|
||||
"vue-router": "~4.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^18.4.1",
|
||||
"@commitlint/config-conventional": "^18.4.0",
|
||||
"@commitlint/cli": "^18.6.0",
|
||||
"@commitlint/config-conventional": "^18.6.0",
|
||||
"@types/echarts": "^4.9.21",
|
||||
"@types/lodash": "^4.14.201",
|
||||
"@types/nprogress": "^0.2.3",
|
||||
"@types/qs": "^6.9.10",
|
||||
"@typescript-eslint/eslint-plugin": "^6.11.0",
|
||||
"@typescript-eslint/parser": "^6.11.0",
|
||||
"@vitejs/plugin-vue": "^4.4.1",
|
||||
"@types/qs": "^6.9.11",
|
||||
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
||||
"@typescript-eslint/parser": "^6.21.0",
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
"@vitejs/plugin-vue-jsx": "^3.0.2",
|
||||
"@vue/compiler-sfc": "^3.3.8",
|
||||
"@vue/compiler-sfc": "~3.3.8",
|
||||
"@vue/eslint-config-typescript": "^12.0.0",
|
||||
"commitizen": "^4.3.0",
|
||||
"cz-conventional-changelog": "^3.3.0",
|
||||
"eslint": "^8.53.0",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-config-airbnb-base": "^15.0.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-plugin-import": "^2.29.0",
|
||||
"eslint-plugin-prettier": "^5.0.1",
|
||||
"eslint-plugin-simple-import-sort": "^10.0.0",
|
||||
"eslint-plugin-vue": "^9.18.1",
|
||||
"eslint-plugin-vue-scoped-css": "^2.5.1",
|
||||
"husky": "^8.0.3",
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-plugin-simple-import-sort": "^12.0.0",
|
||||
"eslint-plugin-vue": "^9.21.1",
|
||||
"eslint-plugin-vue-scoped-css": "^2.7.2",
|
||||
"husky": "^9.0.10",
|
||||
"less": "^4.2.0",
|
||||
"lint-staged": "^15.1.0",
|
||||
"lint-staged": "^15.2.2",
|
||||
"mockjs": "^1.1.0",
|
||||
"postcss-html": "^1.5.0",
|
||||
"postcss-html": "^1.6.0",
|
||||
"postcss-less": "^6.0.0",
|
||||
"prettier": "^3.1.0",
|
||||
"stylelint": "~15.11.0",
|
||||
"stylelint-config-standard": "^34.0.0",
|
||||
"stylelint-order": "~6.0.3",
|
||||
"typescript": "~5.3.2",
|
||||
"vite": "^4.5.0",
|
||||
"vite-plugin-mock": "^3.0.0",
|
||||
"vite-svg-loader": "^4.0.0",
|
||||
"vue-tsc": "^1.8.22"
|
||||
"prettier": "^3.2.5",
|
||||
"stylelint": "~16.2.1",
|
||||
"stylelint-config-standard": "^36.0.0",
|
||||
"stylelint-order": "~6.0.4",
|
||||
"typescript": "~5.4.3",
|
||||
"vite": "^5.1.0",
|
||||
"vite-plugin-mock": "^3.0.1",
|
||||
"vite-svg-loader": "^5.1.0",
|
||||
"vue-tsc": "^1.8.27"
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
|
@ -90,6 +92,6 @@
|
|||
]
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.0.0"
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<t-config-provider :global-config="getComponentsLocale">
|
||||
<router-view :key="locale" :class="[mode]"
|
||||
/></t-config-provider>
|
||||
<router-view :key="locale" :class="[mode]" />
|
||||
</t-config-provider>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
|
|
|
@ -5,6 +5,7 @@ export default {
|
|||
mode: 'light',
|
||||
layout: 'side',
|
||||
splitMenu: false,
|
||||
sideMode: 'light',
|
||||
isFooterAside: false,
|
||||
isSidebarFixed: true,
|
||||
isHeaderFixed: true,
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
import debounce from 'lodash/debounce';
|
||||
import { onMounted, onUnmounted } from 'vue';
|
||||
|
||||
interface WindowSizeOptions {
|
||||
immediate?: boolean;
|
||||
}
|
||||
|
||||
interface Fn<T = any, R = T> {
|
||||
(...arg: T[]): R;
|
||||
}
|
||||
|
||||
export function useWindowSizeFn<T>(fn: Fn<T>, options?: WindowSizeOptions, wait = 150) {
|
||||
const handleSize: () => void = debounce(fn, wait);
|
||||
|
||||
const start = () => {
|
||||
if (options && options.immediate) {
|
||||
fn();
|
||||
}
|
||||
window.addEventListener('resize', handleSize);
|
||||
};
|
||||
|
||||
const stop = () => {
|
||||
window.removeEventListener('resize', handleSize);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
start();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
stop();
|
||||
});
|
||||
return [start, stop];
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<router-view v-if="!isRefreshing" v-slot="{ Component }">
|
||||
<transition name="fade">
|
||||
<transition name="fade" mode="out-in">
|
||||
<keep-alive :include="aliveViews">
|
||||
<component :is="Component" />
|
||||
</keep-alive>
|
||||
|
@ -54,7 +54,7 @@ const isRefreshing = computed(() => {
|
|||
transition: opacity @anim-duration-slow @anim-time-fn-easing;
|
||||
}
|
||||
|
||||
.fade-enter,
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
|
|
@ -6,17 +6,19 @@
|
|||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { useWindowSize } from '@vueuse/core';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { computed, CSSProperties, ref, unref, watch } from 'vue';
|
||||
|
||||
import { prefix } from '@/config/global';
|
||||
import { useWindowSizeFn } from '@/hooks/event/useWindowSizeFn';
|
||||
import { useSettingStore } from '@/store';
|
||||
|
||||
defineProps({
|
||||
frameSrc: String,
|
||||
});
|
||||
|
||||
const { width, height } = useWindowSize();
|
||||
|
||||
const loading = ref(true);
|
||||
const heightRef = ref(window.innerHeight);
|
||||
const frameRef = ref<HTMLFrameElement>();
|
||||
|
@ -69,8 +71,8 @@ function hideLoading() {
|
|||
calcHeight();
|
||||
}
|
||||
|
||||
useWindowSizeFn(calcHeight, { immediate: true });
|
||||
|
||||
// 如果窗口大小发生变化
|
||||
watch([width, height], debounce(calcHeight, 250));
|
||||
watch(
|
||||
[() => settingStore.showFooter, () => settingStore.isUseTabsRouter, () => settingStore.showBreadcrumb],
|
||||
debounce(calcHeight, 250),
|
||||
|
|
|
@ -38,9 +38,13 @@
|
|||
<translate-icon />
|
||||
</t-button>
|
||||
<t-dropdown-menu>
|
||||
<t-dropdown-item v-for="(lang, index) in langList" :key="index" :value="lang.value" @click="changeLang">{{
|
||||
lang.content
|
||||
}}</t-dropdown-item></t-dropdown-menu
|
||||
<t-dropdown-item
|
||||
v-for="(lang, index) in langList"
|
||||
:key="index"
|
||||
:value="lang.value"
|
||||
@click="(options) => changeLang(options.value as string)"
|
||||
>{{ lang.content }}</t-dropdown-item
|
||||
></t-dropdown-menu
|
||||
>
|
||||
</t-dropdown>
|
||||
<t-dropdown :min-column-width="120" trigger="click">
|
||||
|
@ -85,7 +89,7 @@ import { langList } from '@/locales/index';
|
|||
import { useLocale } from '@/locales/useLocale';
|
||||
import { getActive } from '@/router';
|
||||
import { useSettingStore, useUserStore } from '@/store';
|
||||
import type { MenuRoute } from '@/types/interface';
|
||||
import type { MenuRoute, ModeType } from '@/types/interface';
|
||||
|
||||
import MenuContent from './MenuContent.vue';
|
||||
import Notice from './Notice.vue';
|
||||
|
@ -147,11 +151,11 @@ const menuCls = computed(() => {
|
|||
},
|
||||
];
|
||||
});
|
||||
const menuTheme = computed(() => props.theme as 'light' | 'dark');
|
||||
const menuTheme = computed(() => props.theme as ModeType);
|
||||
|
||||
// 切换语言
|
||||
const { changeLocale } = useLocale();
|
||||
const changeLang = ({ value: lang }: { value: string }) => {
|
||||
const changeLang = (lang: string) => {
|
||||
changeLocale(lang);
|
||||
};
|
||||
|
||||
|
@ -238,7 +242,6 @@ const navToHelper = () => {
|
|||
display: flex;
|
||||
align-items: normal;
|
||||
line-height: 0;
|
||||
padding-left: var(--td-comp-margin-xl);
|
||||
}
|
||||
|
||||
.header-logo-container {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
:class="`${prefix}-layout-tabs-nav`"
|
||||
:value="$route.path"
|
||||
:style="{ position: 'sticky', top: 0, width: '100%' }"
|
||||
@change="handleChangeCurrentTab"
|
||||
@change="(value) => handleChangeCurrentTab(value as string)"
|
||||
@remove="handleRemove"
|
||||
@drag-sort="handleDragend"
|
||||
>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
:layout="settingStore.layout"
|
||||
:is-fixed="settingStore.isSidebarFixed"
|
||||
:menu="sideMenu"
|
||||
:theme="settingStore.displayMode"
|
||||
:theme="settingStore.displaySideMode"
|
||||
:is-compact="settingStore.isSidebarCompact"
|
||||
/>
|
||||
</template>
|
||||
|
|
|
@ -3,12 +3,12 @@
|
|||
<t-menu :class="menuCls" :theme="theme" :value="active" :collapsed="collapsed" :default-expanded="defaultExpanded">
|
||||
<template #logo>
|
||||
<span v-if="showLogo" :class="`${prefix}-side-nav-logo-wrapper`" @click="goHome">
|
||||
<component :is="getLogo()" :class="`${prefix}-side-nav-logo-${collapsed ? 't' : 'tdesign'}-logo`" />
|
||||
<component :is="getLogo()" :class="logoCls" />
|
||||
</span>
|
||||
</template>
|
||||
<menu-content :nav-data="menu" />
|
||||
<template #operations>
|
||||
<span class="version-container"> {{ !collapsed ? 'TDesign Starter' : '' }} {{ pgk.version }} </span>
|
||||
<span :class="versionCls"> {{ !collapsed ? 'TDesign Starter' : '' }} {{ pgk.version }} </span>
|
||||
</template>
|
||||
</t-menu>
|
||||
<div :class="`${prefix}-side-nav-placeholder${collapsed ? '-hidden' : ''}`"></div>
|
||||
|
@ -26,7 +26,7 @@ import AssetLogo from '@/assets/assets-t-logo.svg?component';
|
|||
import { prefix } from '@/config/global';
|
||||
import { getActive, getRoutesExpanded } from '@/router';
|
||||
import { useSettingStore } from '@/store';
|
||||
import type { MenuRoute } from '@/types/interface';
|
||||
import type { MenuRoute, ModeType } from '@/types/interface';
|
||||
|
||||
import pgk from '../../../package.json';
|
||||
import MenuContent from './MenuContent.vue';
|
||||
|
@ -55,7 +55,7 @@ const props = defineProps({
|
|||
default: '64px',
|
||||
},
|
||||
theme: {
|
||||
type: String as PropType<'light' | 'dark'>,
|
||||
type: String as PropType<ModeType>,
|
||||
default: 'light',
|
||||
},
|
||||
isCompact: {
|
||||
|
@ -75,6 +75,10 @@ const defaultExpanded = computed(() => {
|
|||
return union(expanded, parentPath === '' ? [] : [parentPath]);
|
||||
});
|
||||
|
||||
const sideMode = computed(() => {
|
||||
const { theme } = props;
|
||||
return theme === 'dark';
|
||||
});
|
||||
const sideNavCls = computed(() => {
|
||||
const { isCompact } = props;
|
||||
return [
|
||||
|
@ -84,7 +88,22 @@ const sideNavCls = computed(() => {
|
|||
},
|
||||
];
|
||||
});
|
||||
|
||||
const logoCls = computed(() => {
|
||||
return [
|
||||
`${prefix}-side-nav-logo-${collapsed.value ? 't' : 'tdesign'}-logo`,
|
||||
{
|
||||
[`${prefix}-side-nav-dark`]: sideMode.value,
|
||||
},
|
||||
];
|
||||
});
|
||||
const versionCls = computed(() => {
|
||||
return [
|
||||
`version-container`,
|
||||
{
|
||||
[`${prefix}-side-nav-dark`]: sideMode.value,
|
||||
},
|
||||
];
|
||||
});
|
||||
const menuCls = computed(() => {
|
||||
const { showLogo, isFixed, layout } = props;
|
||||
return [
|
||||
|
|
|
@ -52,7 +52,6 @@
|
|||
</t-popup>
|
||||
</div>
|
||||
</t-radio-group>
|
||||
|
||||
<div class="setting-group-title">{{ $t('layout.setting.navigationLayout') }}</div>
|
||||
<t-radio-group v-model="formData.layout">
|
||||
<div v-for="(item, index) in LAYOUT_OPTION" :key="index" class="setting-layout-drawer">
|
||||
|
@ -62,15 +61,24 @@
|
|||
</div>
|
||||
</t-radio-group>
|
||||
|
||||
<t-form-item v-show="formData.layout === 'mix'" label="分割菜单(混合模式下有效)" name="splitMenu">
|
||||
<t-form-item v-show="formData.layout === 'mix'" :label="$t('layout.setting.splitMenu')" name="splitMenu">
|
||||
<t-switch v-model="formData.splitMenu" />
|
||||
</t-form-item>
|
||||
|
||||
<t-form-item v-show="formData.layout === 'mix'" label="固定 Sidebar" name="isSidebarFixed">
|
||||
<t-form-item
|
||||
v-show="formData.layout === 'mix'"
|
||||
:label="$t('layout.setting.fixedSidebar')"
|
||||
name="isSidebarFixed"
|
||||
>
|
||||
<t-switch v-model="formData.isSidebarFixed" />
|
||||
</t-form-item>
|
||||
|
||||
<div class="setting-group-title">{{ $t('layout.setting.element.title') }}</div>
|
||||
<t-form-item :label="$t('layout.setting.sideMode')" name="sideMode">
|
||||
<t-radio-group v-model="formData.sideMode" class="side-mode-radio">
|
||||
<t-radio-button key="light" value="light" :label="$t('layout.setting.theme.options.light')" />
|
||||
<t-radio-button key="dark" value="dark" :label="$t('layout.setting.theme.options.dark')" />
|
||||
</t-radio-group>
|
||||
</t-form-item>
|
||||
<t-form-item
|
||||
v-show="formData.layout === 'side'"
|
||||
:label="$t('layout.setting.element.showHeader')"
|
||||
|
@ -295,6 +303,10 @@ watchEffect(() => {
|
|||
width: 100%;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
&.side-mode-radio {
|
||||
justify-content: end;
|
||||
}
|
||||
}
|
||||
|
||||
.t-radio-group.t-size-m .t-radio-button {
|
||||
|
|
|
@ -33,6 +33,7 @@ export default {
|
|||
},
|
||||
},
|
||||
navigationLayout: 'Navigation Layout',
|
||||
sideMode: 'Side Menu Mode',
|
||||
splitMenu: 'Split Menu(Only Mix mode)',
|
||||
fixedSidebar: 'Fixed Sidebar',
|
||||
element: {
|
||||
|
|
|
@ -33,6 +33,7 @@ export default {
|
|||
},
|
||||
},
|
||||
navigationLayout: '导航布局',
|
||||
sideMode: '侧边栏模式',
|
||||
splitMenu: '分割菜单(混合模式下有效)',
|
||||
fixedSidebar: '固定侧边栏',
|
||||
element: {
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
theme="primary"
|
||||
mode="date"
|
||||
:default-value="LAST_7_DAYS"
|
||||
@change="onCurrencyChange"
|
||||
@change="(value) => onCurrencyChange(value as string[])"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -97,6 +97,25 @@ const renderCountChart = () => {
|
|||
}
|
||||
countChart = echarts.init(countContainer);
|
||||
countChart.setOption(getPieChartDataSet(chartColors.value));
|
||||
|
||||
// 取消之前高亮的图形
|
||||
countChart.dispatchAction({
|
||||
type: 'downplay',
|
||||
seriesIndex: 0,
|
||||
dataIndex: -1,
|
||||
});
|
||||
// 高亮当前图形
|
||||
countChart.dispatchAction({
|
||||
type: 'highlight',
|
||||
seriesIndex: 0,
|
||||
dataIndex: 1,
|
||||
});
|
||||
// 显示 tooltip
|
||||
countChart.dispatchAction({
|
||||
type: 'showTip',
|
||||
seriesIndex: 0,
|
||||
dataIndex: 1,
|
||||
});
|
||||
};
|
||||
|
||||
const renderCharts = () => {
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
theme="primary"
|
||||
mode="date"
|
||||
:default-value="LAST_7_DAYS"
|
||||
@change="onStokeDataChange"
|
||||
@change="(value) => onStokeDataChange(value as string[])"
|
||||
/>
|
||||
</template>
|
||||
<div id="stokeContainer" style="width: 100%; height: 351px" class="dashboard-chart-container"></div>
|
||||
|
|
|
@ -367,8 +367,7 @@ export function getPieChartDataSet({
|
|||
emphasis: {
|
||||
scale: true,
|
||||
label: {
|
||||
show: true,
|
||||
formatter: ['{value|{d}%}', '{name|{b}渠道占比}'].join('\n'),
|
||||
show: false,
|
||||
rich: {
|
||||
value: {
|
||||
color: textColor,
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
theme="primary"
|
||||
mode="date"
|
||||
style="width: 248px"
|
||||
@change="onMaterialChange"
|
||||
@change="(value) => onMaterialChange(value as string[])"
|
||||
/>
|
||||
</template>
|
||||
<div id="lineContainer" style="width: 100%; height: 416px" />
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
<template>
|
||||
<div class="detail-advanced">
|
||||
<t-card :title="$t('pages.detailCard.baseInfo.title')" :bordered="false">
|
||||
<div class="info-block">
|
||||
<div v-for="(item, index) in BASE_INFO_DATA" :key="index" class="info-item">
|
||||
<h1>{{ item.name }}</h1>
|
||||
<t-card :bordered="false">
|
||||
<t-descriptions :title="$t('pages.detailCard.baseInfo.title')">
|
||||
<t-descriptions-item v-for="(item, index) in BASE_INFO_DATA" :key="index" :label="item.name">
|
||||
<span
|
||||
:class="{
|
||||
['inProgress']: item.type && item.type.value === 'inProgress',
|
||||
|
@ -13,8 +12,8 @@
|
|||
<i v-if="item.type && item.type.key === 'contractStatus'" />
|
||||
{{ item.value }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</t-descriptions-item>
|
||||
</t-descriptions>
|
||||
</t-card>
|
||||
|
||||
<!-- 发票进度 -->
|
||||
|
@ -113,8 +112,8 @@
|
|||
<t-dialog v-model:visible="visible" :header="$t('pages.detailCard.baseInfo.title')" @confirm="onConfirm">
|
||||
<template #body>
|
||||
<div class="dialog-info-block">
|
||||
<div v-for="(item, index) in BASE_INFO_DATA" :key="index" class="info-item">
|
||||
<h1>{{ item.name }}</h1>
|
||||
<t-descriptions :column="1">
|
||||
<t-descriptions-item v-for="(item, index) in BASE_INFO_DATA" :key="index" :label="item.name">
|
||||
<span
|
||||
:class="{
|
||||
['inProgress']: item.type && item.type.value === 'inProgress',
|
||||
|
@ -124,7 +123,8 @@
|
|||
<i v-if="item.type && item.type.key === 'contractStatus'" />
|
||||
{{ item.value }}
|
||||
</span>
|
||||
</div>
|
||||
</t-descriptions-item>
|
||||
</t-descriptions>
|
||||
</div>
|
||||
</template>
|
||||
</t-dialog>
|
||||
|
|
|
@ -1,51 +1,4 @@
|
|||
.detail-base {
|
||||
:deep(.t-card) {
|
||||
padding: var(--td-comp-paddingTB-xxl) var(--td-comp-paddingLR-xxl);
|
||||
}
|
||||
|
||||
:deep(.t-card__header) {
|
||||
padding: 0;
|
||||
margin-bottom: var(--td-comp-margin-m);
|
||||
}
|
||||
|
||||
:deep(.t-card__body) {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
:deep(.t-card__title) {
|
||||
font: var(--td-font-title-large);
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
&-info-steps {
|
||||
padding-top: var(--td-comp-margin-xl);
|
||||
}
|
||||
}
|
||||
|
||||
.info-block {
|
||||
column-count: 2;
|
||||
|
||||
.info-item {
|
||||
padding-top: var(--td-comp-margin-xxl);
|
||||
display: flex;
|
||||
color: var(--td-text-color-primary);
|
||||
|
||||
h1 {
|
||||
width: 160px;
|
||||
font: var(--td-font-body-medium);
|
||||
color: var(--td-text-color-secondary);
|
||||
font-weight: normal;
|
||||
text-align: left;
|
||||
|
||||
@media (max-width: @screen-sm-max) {
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
@media (min-width: @screen-md-min) and (max-width: @screen-md-max) {
|
||||
width: 120px;
|
||||
}
|
||||
}
|
||||
|
||||
.t-descriptions {
|
||||
span {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
|
@ -72,41 +25,4 @@
|
|||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dialog-info-block {
|
||||
.info-item {
|
||||
padding: 12px 0;
|
||||
display: flex;
|
||||
|
||||
h1 {
|
||||
width: 84px;
|
||||
font-family: var(--td-font-family);
|
||||
font-size: 14px;
|
||||
color: var(--td-text-color-secondary);
|
||||
text-align: left;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
span {
|
||||
margin-left: var(--td-comp-margin-xxl);
|
||||
}
|
||||
|
||||
i {
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: var(--td-radius-circle);
|
||||
background: var(--td-success-color);
|
||||
}
|
||||
|
||||
.green {
|
||||
color: var(--td-success-color);
|
||||
}
|
||||
|
||||
.blue {
|
||||
color: var(--td-brand-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
<template>
|
||||
<div class="detail-base">
|
||||
<t-card :title="$t('pages.detailBase.baseInfo.title')" :bordered="false">
|
||||
<div class="info-block">
|
||||
<div v-for="(item, index) in BASE_INFO_DATA" :key="index" class="info-item">
|
||||
<h1>{{ item.name }}</h1>
|
||||
<t-card :bordered="false">
|
||||
<t-descriptions :title="$t('pages.detailBase.baseInfo.title')">
|
||||
<t-descriptions-item v-for="(item, index) in BASE_INFO_DATA" :key="index" :label="item.name">
|
||||
<span
|
||||
:class="{
|
||||
['inProgress']: item.type && item.type.value === 'inProgress',
|
||||
|
@ -13,8 +12,8 @@
|
|||
<i v-if="item.type && item.type.key === 'contractStatus'" />
|
||||
{{ item.value }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</t-descriptions-item>
|
||||
</t-descriptions>
|
||||
</t-card>
|
||||
|
||||
<t-card :title="$t('pages.detailBase.changelog.title')" class="container-base-margin-top" :bordered="false">
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
:header-affixed-top="headerAffixedTop"
|
||||
@page-change="rehandlePageChange"
|
||||
@change="rehandleChange"
|
||||
@select-change="rehandleSelectChange"
|
||||
@select-change="(value: number[]) => rehandleSelectChange(value)"
|
||||
>
|
||||
<template #status="{ row }">
|
||||
<t-tag v-if="row.status === CONTRACT_STATUS.FAIL" theme="danger" variant="light">
|
||||
|
|
|
@ -74,7 +74,7 @@ const formVisible = ref(false);
|
|||
const formData = ref({ ...INITIAL_DATA });
|
||||
const textareaValue = ref('');
|
||||
|
||||
const onSubmit = ({ validateResult, firstError }: SubmitContext<FormData>) => {
|
||||
const onSubmit = ({ validateResult, firstError }: SubmitContext) => {
|
||||
if (!firstError) {
|
||||
MessagePlugin.success('提交成功');
|
||||
formVisible.value = false;
|
||||
|
|
|
@ -15,16 +15,11 @@
|
|||
<t-icon name="ellipsis" />
|
||||
</t-button>
|
||||
</template>
|
||||
<t-row class="content" justify="space-between">
|
||||
<t-col v-for="(item, index) in USER_INFO_LIST" :key="index" class="contract" :span="item.span ?? 3">
|
||||
<div class="contract-title">
|
||||
{{ $t(item.title) }}
|
||||
</div>
|
||||
<div class="contract-detail">
|
||||
<t-descriptions :column="4" item-layout="vertical">
|
||||
<t-descriptions-item v-for="(item, index) in USER_INFO_LIST" :key="index" :label="$t(item.title)">
|
||||
{{ item.content }}
|
||||
</div>
|
||||
</t-col>
|
||||
</t-row>
|
||||
</t-descriptions-item>
|
||||
</t-descriptions>
|
||||
</t-card>
|
||||
|
||||
<t-card class="content-container" :bordered="false">
|
||||
|
@ -185,4 +180,8 @@ watch(
|
|||
|
||||
<style lang="less" scoped>
|
||||
@import './index.less';
|
||||
|
||||
.t-descriptions {
|
||||
margin-top: 24px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import uniq from 'lodash/uniq';
|
||||
import { createRouter, createWebHistory, RouteRecordRaw, useRoute } from 'vue-router';
|
||||
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
|
||||
|
||||
const env = import.meta.env.MODE || 'development';
|
||||
|
||||
|
@ -59,7 +59,8 @@ export const getRoutesExpanded = () => {
|
|||
};
|
||||
|
||||
export const getActive = (maxLevel = 3): string => {
|
||||
const route = useRoute();
|
||||
// 非组件内调用必须通过Router实例获取当前路由
|
||||
const route = router.currentRoute.value;
|
||||
|
||||
if (!route.path) {
|
||||
return '';
|
||||
|
|
|
@ -2,15 +2,16 @@ import keys from 'lodash/keys';
|
|||
import { defineStore } from 'pinia';
|
||||
import { Color } from 'tvision-color';
|
||||
|
||||
import { DARK_CHART_COLORS, LIGHT_CHART_COLORS } from '@/config/color';
|
||||
import { DARK_CHART_COLORS, LIGHT_CHART_COLORS, TColorSeries } from '@/config/color';
|
||||
import STYLE_CONFIG from '@/config/style';
|
||||
import { store } from '@/store';
|
||||
import { ModeType } from '@/types/interface';
|
||||
import { generateColorMap, insertThemeStylesheet } from '@/utils/color';
|
||||
|
||||
const state = {
|
||||
const state: Record<string, any> = {
|
||||
...STYLE_CONFIG,
|
||||
showSettingPanel: false,
|
||||
colorList: {},
|
||||
colorList: {} as TColorSeries,
|
||||
chartColors: LIGHT_CHART_COLORS,
|
||||
};
|
||||
|
||||
|
@ -23,7 +24,7 @@ export const useSettingStore = defineStore('setting', {
|
|||
showSidebar: (state) => state.layout !== 'top',
|
||||
showSidebarLogo: (state) => state.layout === 'side',
|
||||
showHeaderLogo: (state) => state.layout !== 'side',
|
||||
displayMode: (state): 'dark' | 'light' => {
|
||||
displayMode: (state): ModeType => {
|
||||
if (state.mode === 'auto') {
|
||||
const media = window.matchMedia('(prefers-color-scheme:dark)');
|
||||
if (media.matches) {
|
||||
|
@ -31,20 +32,18 @@ export const useSettingStore = defineStore('setting', {
|
|||
}
|
||||
return 'light';
|
||||
}
|
||||
return state.mode as 'dark' | 'light';
|
||||
return state.mode as ModeType;
|
||||
},
|
||||
displaySideMode: (state): ModeType => {
|
||||
return state.sideMode as ModeType;
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
async changeMode(mode: 'dark' | 'light' | 'auto') {
|
||||
async changeMode(mode: ModeType | 'auto') {
|
||||
let theme = mode;
|
||||
|
||||
if (mode === 'auto') {
|
||||
const media = window.matchMedia('(prefers-color-scheme:dark)');
|
||||
if (media.matches) {
|
||||
theme = 'dark';
|
||||
} else {
|
||||
theme = 'light';
|
||||
}
|
||||
theme = this.getMediaColor();
|
||||
}
|
||||
const isDarkMode = theme === 'dark';
|
||||
|
||||
|
@ -52,6 +51,19 @@ export const useSettingStore = defineStore('setting', {
|
|||
|
||||
this.chartColors = isDarkMode ? DARK_CHART_COLORS : LIGHT_CHART_COLORS;
|
||||
},
|
||||
async changeSideMode(mode: ModeType) {
|
||||
const isDarkMode = mode === 'dark';
|
||||
|
||||
document.documentElement.setAttribute('side-mode', isDarkMode ? 'dark' : '');
|
||||
},
|
||||
getMediaColor() {
|
||||
const media = window.matchMedia('(prefers-color-scheme:dark)');
|
||||
|
||||
if (media.matches) {
|
||||
return 'dark';
|
||||
}
|
||||
return 'light';
|
||||
},
|
||||
changeBrandTheme(brandTheme: string) {
|
||||
const mode = this.displayMode;
|
||||
// 以主题色加显示模式作为键
|
||||
|
@ -64,20 +76,23 @@ export const useSettingStore = defineStore('setting', {
|
|||
step: 10,
|
||||
remainInput: false, // 是否保留输入 不保留会矫正不合适的主题色
|
||||
});
|
||||
colorMap = generateColorMap(brandTheme, newPalette, mode as 'light' | 'dark', brandColorIndex);
|
||||
colorMap = generateColorMap(brandTheme, newPalette, mode, brandColorIndex);
|
||||
this.colorList[colorKey] = colorMap;
|
||||
}
|
||||
// TODO 需要解决不停切换时有反复插入 style 的问题
|
||||
insertThemeStylesheet(brandTheme, colorMap, mode as 'light' | 'dark');
|
||||
insertThemeStylesheet(brandTheme, colorMap, mode);
|
||||
document.documentElement.setAttribute('theme-color', brandTheme);
|
||||
},
|
||||
updateConfig(payload: Partial<TState>) {
|
||||
for (const key in payload) {
|
||||
if (payload[key as TStateKey] !== undefined) {
|
||||
this[key] = payload[key as TStateKey];
|
||||
this[key as TStateKey] = payload[key as TStateKey];
|
||||
}
|
||||
if (key === 'mode') {
|
||||
this.changeMode(payload[key]);
|
||||
this.changeMode(payload[key] as ModeType);
|
||||
}
|
||||
if (key === 'sideMode') {
|
||||
this.changeSideMode(payload[key] as ModeType);
|
||||
}
|
||||
if (key === 'brandTheme') {
|
||||
this.changeBrandTheme(payload[key]);
|
||||
|
|
|
@ -163,7 +163,7 @@
|
|||
}
|
||||
|
||||
&-logo-tdesign-logo {
|
||||
padding: 0 var(--td-comp-paddingLR-xl);
|
||||
margin-right: var(--td-comp-margin-xxxl);
|
||||
height: var(--td-comp-size-s);
|
||||
width: 100%;
|
||||
color: var(--td-text-color-primary);
|
||||
|
@ -176,6 +176,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
&-side-nav-dark {
|
||||
color: var(--td-font-white-1) !important;
|
||||
}
|
||||
|
||||
&-side-nav-placeholder {
|
||||
flex: 1 1 232px;
|
||||
min-width: 232px;
|
||||
|
|
10
src/types/interface.d.ts
vendored
10
src/types/interface.d.ts
vendored
|
@ -1,8 +1,6 @@
|
|||
import type { TabValue } from 'tdesign-vue-next';
|
||||
import { LocationQueryRaw, RouteRecordName } from 'vue-router';
|
||||
|
||||
import STYLE_CONFIG from '@/config/style';
|
||||
|
||||
export interface RouteMeta {
|
||||
title?: string | Record<string, string>;
|
||||
icon?: string;
|
||||
|
@ -32,14 +30,6 @@ export interface MenuRoute {
|
|||
|
||||
export type ModeType = 'dark' | 'light';
|
||||
|
||||
export type SettingType = typeof STYLE_CONFIG;
|
||||
|
||||
export type ClassName = { [className: string]: any } | ClassName[] | string;
|
||||
|
||||
export type CommonObjType = {
|
||||
[key: string]: string | number;
|
||||
};
|
||||
|
||||
export interface UserInfo {
|
||||
name: string;
|
||||
roles: string[];
|
||||
|
|
|
@ -3,6 +3,7 @@ import trim from 'lodash/trim';
|
|||
import { Color } from 'tvision-color';
|
||||
|
||||
import { TColorToken } from '@/config/color';
|
||||
import { ModeType } from '@/types/interface';
|
||||
|
||||
/**
|
||||
* 依据主题类型获取颜色
|
||||
|
@ -58,12 +59,7 @@ export function changeChartsTheme(chartsList: echarts.EChartsType[]): void {
|
|||
/**
|
||||
* 根据当前主题色、模式等情景 计算最后生成的色阶
|
||||
*/
|
||||
export function generateColorMap(
|
||||
theme: string,
|
||||
colorPalette: Array<string>,
|
||||
mode: 'light' | 'dark',
|
||||
brandColorIdx: number,
|
||||
) {
|
||||
export function generateColorMap(theme: string, colorPalette: Array<string>, mode: ModeType, brandColorIdx: number) {
|
||||
const isDarkMode = mode === 'dark';
|
||||
|
||||
if (isDarkMode) {
|
||||
|
@ -76,7 +72,7 @@ export function generateColorMap(
|
|||
colorPalette[0] = `${colorPalette[brandColorIdx]}20`;
|
||||
}
|
||||
|
||||
const colorMap = {
|
||||
const colorMap: TColorToken = {
|
||||
'--td-brand-color': colorPalette[brandColorIdx], // 主题色
|
||||
'--td-brand-color-1': colorPalette[0], // light
|
||||
'--td-brand-color-2': colorPalette[1], // focus
|
||||
|
@ -95,7 +91,7 @@ export function generateColorMap(
|
|||
/**
|
||||
* 将生成的样式嵌入头部
|
||||
*/
|
||||
export function insertThemeStylesheet(theme: string, colorMap: TColorToken, mode: 'light' | 'dark') {
|
||||
export function insertThemeStylesheet(theme: string, colorMap: TColorToken, mode: ModeType) {
|
||||
const isDarkMode = mode === 'dark';
|
||||
const root = !isDarkMode ? `:root[theme-color='${theme}']` : `:root[theme-color='${theme}'][theme-mode='dark']`;
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
module.exports = {
|
||||
export default {
|
||||
defaultSeverity: 'error',
|
||||
extends: ['stylelint-config-standard'],
|
||||
rules: {
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"jsx": "preserve",
|
||||
"jsxImportSource": "vue",
|
||||
"sourceMap": true,
|
||||
"resolveJsonModule": true,
|
||||
"esModuleInterop": true,
|
||||
|
@ -16,7 +17,11 @@
|
|||
"paths": {
|
||||
"@/*": ["src/*"]
|
||||
},
|
||||
"noImplicitAny": true
|
||||
"noImplicitAny": true,
|
||||
"strictFunctionTypes": true,
|
||||
"strictBindCallApply": true,
|
||||
"noImplicitThis": true,
|
||||
"alwaysStrict": true,
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
|
|
Loading…
Reference in New Issue
Block a user