[improvement][semantic-fe] Upgrading antd version to 5.x, extracting the batch operation button component, optimizing the interaction for system settings, and expanding the configuration generation types for list-to-select component. (#404)

* [improvement][semantic-fe] Add model alias setting & Add view permission restrictions to the model permission management tab.
[improvement][semantic-fe] Add permission control to the action buttons for the main domain; apply high sensitivity filtering to the authorization of metrics/dimensions.
[improvement][semantic-fe] Optimize the editing mode in the dimension/metric/datasource components to use the modelId stored in the database for data, instead of relying on the data from the state manager.

* [improvement][semantic-fe] Add time granularity setting in the data source configuration.

* [improvement][semantic-fe] Dictionary import for dimension values supported in Q&A visibility

* [improvement][semantic-fe] Modification of data source creation prompt wording"

* [improvement][semantic-fe] metric market experience optimization

* [improvement][semantic-fe] enhance the analysis of metric trends

* [improvement][semantic-fe] optimize the presentation of metric trend permissions

* [improvement][semantic-fe] add metric trend download functionality

* [improvement][semantic-fe] fix the dimension initialization issue in metric correlation

* [improvement][semantic-fe] Fix the issue of database changes not taking effect when creating based on an SQL data source.

* [improvement][semantic-fe] Optimizing pagination logic and some CSS styles

* [improvement][semantic-fe] Fixing the API for the indicator list by changing "current" to "pageNum"

* [improvement][semantic-fe] Fixing the default value setting for the indicator list

* [improvement][semantic-fe] Adding batch operations for indicators/dimensions/models

* [improvement][semantic-fe] Replacing the single status update API for indicators/dimensions with a batch update API

* [improvement][semantic-fe] Redesigning the indicator homepage to incorporate trend charts and table functionality for indicators

* [improvement][semantic-fe] Optimizing the logic for setting dimension values and editing data sources, and adding system settings functionality

* [improvement][semantic-fe] Upgrading antd version to 5.x, extracting the batch operation button component, optimizing the interaction for system settings, and expanding the configuration generation types for list-to-select component.
This commit is contained in:
tristanliu
2023-11-20 12:04:08 +08:00
committed by GitHub
parent 0143b0a1b2
commit 80ad75503b
84 changed files with 9472 additions and 737 deletions

View File

@@ -61,6 +61,9 @@ export default defineConfig({
theme: {
...themeSettings,
},
alias: {
'antd/es/style': '@/assets/style',
},
esbuild: {},
title: false,
ignoreMomentLocale: true,

View File

@@ -4,8 +4,8 @@ const Settings: LayoutSettings & {
pwa?: boolean;
logo?: string;
} = {
navTheme: 'dark',
primaryColor: '#296DF3',
navTheme: 'light',
colorPrimary: '#296DF3',
layout: 'top',
contentWidth: 'Fluid',
fixedHeader: false,

View File

@@ -52,15 +52,11 @@
"not ie <= 10"
],
"dependencies": {
"@ant-design/charts": "^1.3.3",
"@ant-design/cssinjs": "^1.10.1",
"@ant-design/icons": "^4.7.0",
"@ant-design/pro-card": "^1.11.13",
"@ant-design/pro-components": "^2.4.4",
"@ant-design/pro-descriptions": "^1.0.19",
"@ant-design/pro-form": "^1.23.0",
"@ant-design/pro-layout": "^6.38.22",
"@ant-design/pro-table": "^2.80.6",
"@ant-design/icons": "^5.2.6",
"@ant-design/pro-card": "^2.5.26",
"@ant-design/pro-components": "^2.6.41",
"@ant-design/pro-layout": "^7.17.15",
"@ant-design/pro-table": "^3.13.9",
"@antv/dom-util": "^2.0.4",
"@antv/g6": "^4.8.14",
"@antv/g6-core": "^0.8.23",
@@ -73,7 +69,7 @@
"@umijs/route-utils": "^1.0.33",
"ace-builds": "^1.4.12",
"ahooks": "^3.7.7",
"antd": "^4.24.8",
"antd": "^5.11.0",
"classnames": "^2.2.6",
"copy-to-clipboard": "^3.3.1",
"cross-env": "^7.0.3",

View File

@@ -133,7 +133,7 @@ export const layout: RunTimeLayoutConfig = (params) => {
>
{dom}
{history.location.pathname !== '/chat' && !isMobile && (
<Copilot token={getToken() || ''} isDeveloper />
<Copilot token={getAuthToken() || ''} isDeveloper />
)}
</div>
);

View File

@@ -1,4 +1,4 @@
@import '~antd/lib/style/themes/default';
// main
@primary: #225ace;

View File

@@ -0,0 +1,110 @@
/* stylelint-disable */
.bezierEasingMixin() {
@functions: ~`(function() {
var NEWTON_ITERATIONS = 4;
var NEWTON_MIN_SLOPE = 0.001;
var SUBDIVISION_PRECISION = 0.0000001;
var SUBDIVISION_MAX_ITERATIONS = 10;
var kSplineTableSize = 11;
var kSampleStepSize = 1.0 / (kSplineTableSize - 1.0);
var float32ArraySupported = typeof Float32Array === 'function';
function A (aA1, aA2) { return 1.0 - 3.0 * aA2 + 3.0 * aA1; }
function B (aA1, aA2) { return 3.0 * aA2 - 6.0 * aA1; }
function C (aA1) { return 3.0 * aA1; }
// Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
function calcBezier (aT, aA1, aA2) { return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT; }
// Returns dx/dt given t, x1, and x2, or dy/dt given t, y1, and y2.
function getSlope (aT, aA1, aA2) { return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1); }
function binarySubdivide (aX, aA, aB, mX1, mX2) {
var currentX, currentT, i = 0;
do {
currentT = aA + (aB - aA) / 2.0;
currentX = calcBezier(currentT, mX1, mX2) - aX;
if (currentX > 0.0) {
aB = currentT;
} else {
aA = currentT;
}
} while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS);
return currentT;
}
function newtonRaphsonIterate (aX, aGuessT, mX1, mX2) {
for (var i = 0; i < NEWTON_ITERATIONS; ++i) {
var currentSlope = getSlope(aGuessT, mX1, mX2);
if (currentSlope === 0.0) {
return aGuessT;
}
var currentX = calcBezier(aGuessT, mX1, mX2) - aX;
aGuessT -= currentX / currentSlope;
}
return aGuessT;
}
var BezierEasing = function (mX1, mY1, mX2, mY2) {
if (!(0 <= mX1 && mX1 <= 1 && 0 <= mX2 && mX2 <= 1)) {
throw new Error('bezier x values must be in [0, 1] range');
}
// Precompute samples table
var sampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize);
if (mX1 !== mY1 || mX2 !== mY2) {
for (var i = 0; i < kSplineTableSize; ++i) {
sampleValues[i] = calcBezier(i * kSampleStepSize, mX1, mX2);
}
}
function getTForX (aX) {
var intervalStart = 0.0;
var currentSample = 1;
var lastSample = kSplineTableSize - 1;
for (; currentSample !== lastSample && sampleValues[currentSample] <= aX; ++currentSample) {
intervalStart += kSampleStepSize;
}
--currentSample;
// Interpolate to provide an initial guess for t
var dist = (aX - sampleValues[currentSample]) / (sampleValues[currentSample + 1] - sampleValues[currentSample]);
var guessForT = intervalStart + dist * kSampleStepSize;
var initialSlope = getSlope(guessForT, mX1, mX2);
if (initialSlope >= NEWTON_MIN_SLOPE) {
return newtonRaphsonIterate(aX, guessForT, mX1, mX2);
} else if (initialSlope === 0.0) {
return guessForT;
} else {
return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize, mX1, mX2);
}
}
return function BezierEasing (x) {
if (mX1 === mY1 && mX2 === mY2) {
return x; // linear
}
// Because JavaScript number are imprecise, we should guarantee the extremes are right.
if (x === 0) {
return 0;
}
if (x === 1) {
return 1;
}
return calcBezier(getTForX(x), mY1, mY2);
};
};
this.colorEasing = BezierEasing(0.26, 0.09, 0.37, 0.18);
// less 3 requires a return
return '';
})()`;
}
// It is hacky way to make this function will be compiled preferentially by less
// resolve error: `ReferenceError: colorPalette is not defined`
// https://github.com/ant-design/ant-motion/issues/44
.bezierEasingMixin();

View File

@@ -0,0 +1,85 @@
/* stylelint-disable no-duplicate-selectors */
@import "bezierEasing";
@import "tinyColor";
// We create a very complex algorithm which take the place of original tint/shade color system
// to make sure no one can understand it 👻
// and create an entire color palette magicly by inputing just a single primary color.
// We are using bezier-curve easing function and some color manipulations like tint/shade/darken/spin
.colorPaletteMixin() {
@functions: ~`(function() {
var hueStep = 2;
var saturationStep = 0.16;
var saturationStep2 = 0.05;
var brightnessStep1 = 0.05;
var brightnessStep2 = 0.15;
var lightColorCount = 5;
var darkColorCount = 4;
var getHue = function(hsv, i, isLight) {
var hue;
if (hsv.h >= 60 && hsv.h <= 240) {
hue = isLight ? hsv.h - hueStep * i : hsv.h + hueStep * i;
} else {
hue = isLight ? hsv.h + hueStep * i : hsv.h - hueStep * i;
}
if (hue < 0) {
hue += 360;
} else if (hue >= 360) {
hue -= 360;
}
return Math.round(hue);
};
var getSaturation = function(hsv, i, isLight) {
// grey color don't change saturation
if (hsv.h === 0 && hsv.s === 0) {
return hsv.s;
}
var saturation;
if (isLight) {
saturation = hsv.s - saturationStep * i;
} else if (i === darkColorCount) {
saturation = hsv.s + saturationStep;
} else {
saturation = hsv.s + saturationStep2 * i;
}
if (saturation > 1) {
saturation = 1;
}
if (isLight && i === lightColorCount && saturation > 0.1) {
saturation = 0.1;
}
if (saturation < 0.06) {
saturation = 0.06;
}
return Number(saturation.toFixed(2));
};
var getValue = function(hsv, i, isLight) {
var value;
if (isLight) {
value = hsv.v + brightnessStep1 * i;
}else{
value = hsv.v - brightnessStep2 * i
}
if (value > 1) {
value = 1;
}
return Number(value.toFixed(2))
};
this.colorPalette = function(color, index) {
var isLight = index <= 6;
var hsv = tinycolor(color).toHsv();
var i = isLight ? lightColorCount + 1 - index : index - lightColorCount - 1;
return tinycolor({
h: getHue(hsv, i, isLight),
s: getSaturation(hsv, i, isLight),
v: getValue(hsv, i, isLight),
}).toHexString();
};
})()`;
}
// It is hacky way to make this function will be compiled preferentially by less
// resolve error: `ReferenceError: colorPalette is not defined`
// https://github.com/ant-design/ant-motion/issues/44
.colorPaletteMixin();

View File

@@ -0,0 +1,162 @@
@import 'colorPalette';
// color palettes
@blue-base: #1890ff;
@blue-1: color(~`colorPalette('@{blue-6}', 1) `);
@blue-2: color(~`colorPalette('@{blue-6}', 2) `);
@blue-3: color(~`colorPalette('@{blue-6}', 3) `);
@blue-4: color(~`colorPalette('@{blue-6}', 4) `);
@blue-5: color(~`colorPalette('@{blue-6}', 5) `);
@blue-6: @blue-base;
@blue-7: color(~`colorPalette('@{blue-6}', 7) `);
@blue-8: color(~`colorPalette('@{blue-6}', 8) `);
@blue-9: color(~`colorPalette('@{blue-6}', 9) `);
@blue-10: color(~`colorPalette('@{blue-6}', 10) `);
@purple-base: #722ed1;
@purple-1: color(~`colorPalette('@{purple-6}', 1) `);
@purple-2: color(~`colorPalette('@{purple-6}', 2) `);
@purple-3: color(~`colorPalette('@{purple-6}', 3) `);
@purple-4: color(~`colorPalette('@{purple-6}', 4) `);
@purple-5: color(~`colorPalette('@{purple-6}', 5) `);
@purple-6: @purple-base;
@purple-7: color(~`colorPalette('@{purple-6}', 7) `);
@purple-8: color(~`colorPalette('@{purple-6}', 8) `);
@purple-9: color(~`colorPalette('@{purple-6}', 9) `);
@purple-10: color(~`colorPalette('@{purple-6}', 10) `);
@cyan-base: #13c2c2;
@cyan-1: color(~`colorPalette('@{cyan-6}', 1) `);
@cyan-2: color(~`colorPalette('@{cyan-6}', 2) `);
@cyan-3: color(~`colorPalette('@{cyan-6}', 3) `);
@cyan-4: color(~`colorPalette('@{cyan-6}', 4) `);
@cyan-5: color(~`colorPalette('@{cyan-6}', 5) `);
@cyan-6: @cyan-base;
@cyan-7: color(~`colorPalette('@{cyan-6}', 7) `);
@cyan-8: color(~`colorPalette('@{cyan-6}', 8) `);
@cyan-9: color(~`colorPalette('@{cyan-6}', 9) `);
@cyan-10: color(~`colorPalette('@{cyan-6}', 10) `);
@green-base: #52c41a;
@green-1: color(~`colorPalette('@{green-6}', 1) `);
@green-2: color(~`colorPalette('@{green-6}', 2) `);
@green-3: color(~`colorPalette('@{green-6}', 3) `);
@green-4: color(~`colorPalette('@{green-6}', 4) `);
@green-5: color(~`colorPalette('@{green-6}', 5) `);
@green-6: @green-base;
@green-7: color(~`colorPalette('@{green-6}', 7) `);
@green-8: color(~`colorPalette('@{green-6}', 8) `);
@green-9: color(~`colorPalette('@{green-6}', 9) `);
@green-10: color(~`colorPalette('@{green-6}', 10) `);
@magenta-base: #eb2f96;
@magenta-1: color(~`colorPalette('@{magenta-6}', 1) `);
@magenta-2: color(~`colorPalette('@{magenta-6}', 2) `);
@magenta-3: color(~`colorPalette('@{magenta-6}', 3) `);
@magenta-4: color(~`colorPalette('@{magenta-6}', 4) `);
@magenta-5: color(~`colorPalette('@{magenta-6}', 5) `);
@magenta-6: @magenta-base;
@magenta-7: color(~`colorPalette('@{magenta-6}', 7) `);
@magenta-8: color(~`colorPalette('@{magenta-6}', 8) `);
@magenta-9: color(~`colorPalette('@{magenta-6}', 9) `);
@magenta-10: color(~`colorPalette('@{magenta-6}', 10) `);
// alias of magenta
@pink-base: #eb2f96;
@pink-1: color(~`colorPalette('@{pink-6}', 1) `);
@pink-2: color(~`colorPalette('@{pink-6}', 2) `);
@pink-3: color(~`colorPalette('@{pink-6}', 3) `);
@pink-4: color(~`colorPalette('@{pink-6}', 4) `);
@pink-5: color(~`colorPalette('@{pink-6}', 5) `);
@pink-6: @pink-base;
@pink-7: color(~`colorPalette('@{pink-6}', 7) `);
@pink-8: color(~`colorPalette('@{pink-6}', 8) `);
@pink-9: color(~`colorPalette('@{pink-6}', 9) `);
@pink-10: color(~`colorPalette('@{pink-6}', 10) `);
@red-base: #f5222d;
@red-1: color(~`colorPalette('@{red-6}', 1) `);
@red-2: color(~`colorPalette('@{red-6}', 2) `);
@red-3: color(~`colorPalette('@{red-6}', 3) `);
@red-4: color(~`colorPalette('@{red-6}', 4) `);
@red-5: color(~`colorPalette('@{red-6}', 5) `);
@red-6: @red-base;
@red-7: color(~`colorPalette('@{red-6}', 7) `);
@red-8: color(~`colorPalette('@{red-6}', 8) `);
@red-9: color(~`colorPalette('@{red-6}', 9) `);
@red-10: color(~`colorPalette('@{red-6}', 10) `);
@orange-base: #fa8c16;
@orange-1: color(~`colorPalette('@{orange-6}', 1) `);
@orange-2: color(~`colorPalette('@{orange-6}', 2) `);
@orange-3: color(~`colorPalette('@{orange-6}', 3) `);
@orange-4: color(~`colorPalette('@{orange-6}', 4) `);
@orange-5: color(~`colorPalette('@{orange-6}', 5) `);
@orange-6: @orange-base;
@orange-7: color(~`colorPalette('@{orange-6}', 7) `);
@orange-8: color(~`colorPalette('@{orange-6}', 8) `);
@orange-9: color(~`colorPalette('@{orange-6}', 9) `);
@orange-10: color(~`colorPalette('@{orange-6}', 10) `);
@yellow-base: #fadb14;
@yellow-1: color(~`colorPalette('@{yellow-6}', 1) `);
@yellow-2: color(~`colorPalette('@{yellow-6}', 2) `);
@yellow-3: color(~`colorPalette('@{yellow-6}', 3) `);
@yellow-4: color(~`colorPalette('@{yellow-6}', 4) `);
@yellow-5: color(~`colorPalette('@{yellow-6}', 5) `);
@yellow-6: @yellow-base;
@yellow-7: color(~`colorPalette('@{yellow-6}', 7) `);
@yellow-8: color(~`colorPalette('@{yellow-6}', 8) `);
@yellow-9: color(~`colorPalette('@{yellow-6}', 9) `);
@yellow-10: color(~`colorPalette('@{yellow-6}', 10) `);
@volcano-base: #fa541c;
@volcano-1: color(~`colorPalette('@{volcano-6}', 1) `);
@volcano-2: color(~`colorPalette('@{volcano-6}', 2) `);
@volcano-3: color(~`colorPalette('@{volcano-6}', 3) `);
@volcano-4: color(~`colorPalette('@{volcano-6}', 4) `);
@volcano-5: color(~`colorPalette('@{volcano-6}', 5) `);
@volcano-6: @volcano-base;
@volcano-7: color(~`colorPalette('@{volcano-6}', 7) `);
@volcano-8: color(~`colorPalette('@{volcano-6}', 8) `);
@volcano-9: color(~`colorPalette('@{volcano-6}', 9) `);
@volcano-10: color(~`colorPalette('@{volcano-6}', 10) `);
@geekblue-base: #2f54eb;
@geekblue-1: color(~`colorPalette('@{geekblue-6}', 1) `);
@geekblue-2: color(~`colorPalette('@{geekblue-6}', 2) `);
@geekblue-3: color(~`colorPalette('@{geekblue-6}', 3) `);
@geekblue-4: color(~`colorPalette('@{geekblue-6}', 4) `);
@geekblue-5: color(~`colorPalette('@{geekblue-6}', 5) `);
@geekblue-6: @geekblue-base;
@geekblue-7: color(~`colorPalette('@{geekblue-6}', 7) `);
@geekblue-8: color(~`colorPalette('@{geekblue-6}', 8) `);
@geekblue-9: color(~`colorPalette('@{geekblue-6}', 9) `);
@geekblue-10: color(~`colorPalette('@{geekblue-6}', 10) `);
@lime-base: #a0d911;
@lime-1: color(~`colorPalette('@{lime-6}', 1) `);
@lime-2: color(~`colorPalette('@{lime-6}', 2) `);
@lime-3: color(~`colorPalette('@{lime-6}', 3) `);
@lime-4: color(~`colorPalette('@{lime-6}', 4) `);
@lime-5: color(~`colorPalette('@{lime-6}', 5) `);
@lime-6: @lime-base;
@lime-7: color(~`colorPalette('@{lime-6}', 7) `);
@lime-8: color(~`colorPalette('@{lime-6}', 8) `);
@lime-9: color(~`colorPalette('@{lime-6}', 9) `);
@lime-10: color(~`colorPalette('@{lime-6}', 10) `);
@gold-base: #faad14;
@gold-1: color(~`colorPalette('@{gold-6}', 1) `);
@gold-2: color(~`colorPalette('@{gold-6}', 2) `);
@gold-3: color(~`colorPalette('@{gold-6}', 3) `);
@gold-4: color(~`colorPalette('@{gold-6}', 4) `);
@gold-5: color(~`colorPalette('@{gold-6}', 5) `);
@gold-6: @gold-base;
@gold-7: color(~`colorPalette('@{gold-6}', 7) `);
@gold-8: color(~`colorPalette('@{gold-6}', 8) `);
@gold-9: color(~`colorPalette('@{gold-6}', 9) `);
@gold-10: color(~`colorPalette('@{gold-6}', 10) `);
@preset-colors: pink, magenta, red, volcano, orange, yellow, gold, cyan, lime, green, blue, geekblue,
purple;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,4 @@
@root-entry-name: default;
@import './themes/compact.less';
@import './core/index';

View File

@@ -0,0 +1,10 @@
// Config global less under antd
[class^=~'@{ant-prefix}-'],
[class*=~' @{ant-prefix}-'] {
// remove the clear button of a text input control in IE10+
&::-ms-clear,
input::-ms-clear,
input::-ms-reveal {
display: none;
}
}

View File

@@ -0,0 +1,491 @@
/* stylelint-disable property-no-vendor-prefix, at-rule-no-vendor-prefix */
// Reboot
//
// Normalization of HTML elements, manually forked from Normalize.css to remove
// styles targeting irrelevant browsers while applying new styles.
//
// Normalize is licensed MIT. https://github.com/necolas/normalize.css
// HTML & Body reset
@{html-selector},
body {
.square(100%);
}
// remove the clear button of a text input control in IE10+
input::-ms-clear,
input::-ms-reveal {
display: none;
}
// Document
//
// 1. Change from `box-sizing: content-box` so that `width` is not affected by `padding` or `border`.
// 2. Change the default font family in all browsers.
// 3. Correct the line height in all browsers.
// 4. Prevent adjustments of font size after orientation changes in IE on Windows Phone and in iOS.
// 5. Setting @viewport causes scrollbars to overlap content in IE11 and Edge, so
// we force a non-overlapping, non-auto-hiding scrollbar to counteract.
// 6. Change the default tap highlight to be completely transparent in iOS.
*,
*::before,
*::after {
box-sizing: border-box; // 1
}
@{html-selector} {
font-family: sans-serif; // 2
line-height: 1.15; // 3
-webkit-text-size-adjust: 100%; // 4
-ms-text-size-adjust: 100%; // 4
-ms-overflow-style: scrollbar; // 5
-webkit-tap-highlight-color: fade(@black, 0%); // 6
}
// IE10+ doesn't honor `<meta name="viewport">` in some cases.
@-ms-viewport {
width: device-width;
}
// Body
//
// 1. remove the margin in all browsers.
// 2. As a best practice, apply a default `body-background`.
body {
margin: 0; // 1
color: @text-color;
font-size: @font-size-base;
font-family: @font-family;
font-variant: @font-variant-base;
line-height: @line-height-base;
background-color: @body-background; // 2
font-feature-settings: @font-feature-settings-base;
}
// Suppress the focus outline on elements that cannot be accessed via keyboard.
// This prevents an unwanted focus outline from appearing around elements that
// might still respond to pointer events.
//
// Credit: https://github.com/suitcss/base
[tabindex='-1']:focus {
outline: none !important;
}
// Content grouping
//
// 1. Add the correct box sizing in Firefox.
// 2. Show the overflow in Edge and IE.
hr {
box-sizing: content-box; // 1
height: 0; // 1
overflow: visible; // 2
}
//
// Typography
//
// remove top margins from headings
//
// By default, `<h1>`-`<h6>` all receive top and bottom margins. We nuke the top
// margin for easier control within type scales as it avoids margin collapsing.
h1,
h2,
h3,
h4,
h5,
h6 {
margin-top: 0;
margin-bottom: 0.5em;
color: @heading-color;
font-weight: 500;
}
// Reset margins on paragraphs
//
// Similarly, the top margin on `<p>`s get reset. However, we also reset the
// bottom margin to use `em` units instead of `em`.
p {
margin-top: 0;
margin-bottom: 1em;
}
// Abbreviations
//
// 1. remove the bottom border in Firefox 39-.
// 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
// 3. Add explicit cursor to indicate changed behavior.
// 4. Duplicate behavior to the data-* attribute for our tooltip plugin
abbr[title],
abbr[data-original-title] {
// 4
text-decoration: underline; // 2
text-decoration: underline dotted; // 2
border-bottom: 0; // 1
cursor: help; // 3
}
address {
margin-bottom: 1em;
font-style: normal;
line-height: inherit;
}
input[type='text'],
input[type='password'],
input[type='number'],
textarea {
-webkit-appearance: none;
}
ol,
ul,
dl {
margin-top: 0;
margin-bottom: 1em;
}
ol ol,
ul ul,
ol ul,
ul ol {
margin-bottom: 0;
}
dt {
font-weight: 500;
}
dd {
margin-bottom: 0.5em;
margin-left: 0; // Undo browser default
}
blockquote {
margin: 0 0 1em;
}
dfn {
font-style: italic; // Add the correct font style in Android 4.3-
}
b,
strong {
font-weight: bolder; // Add the correct font weight in Chrome, Edge, and Safari
}
small {
font-size: 80%; // Add the correct font size in all browsers
}
//
// Prevent `sub` and `sup` elements from affecting the line height in
// all browsers.
//
sub,
sup {
position: relative;
font-size: 75%;
line-height: 0;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
//
// Links
//
a {
color: @link-color;
text-decoration: @link-decoration;
background-color: transparent; // remove the gray background on active links in IE 10.
outline: none;
cursor: pointer;
transition: color 0.3s;
-webkit-text-decoration-skip: objects; // remove gaps in links underline in iOS 8+ and Safari 8+.
&:hover {
color: @link-hover-color;
}
&:active {
color: @link-active-color;
}
&:active,
&:hover {
text-decoration: @link-hover-decoration;
outline: 0;
}
// https://github.com/ant-design/ant-design/issues/22503
&:focus {
text-decoration: @link-focus-decoration;
outline: @link-focus-outline;
}
&[disabled] {
color: @disabled-color;
cursor: not-allowed;
}
}
//
// Code
//
pre,
code,
kbd,
samp {
font-size: 1em; // Correct the odd `em` font sizing in all browsers.
font-family: @code-family;
}
pre {
// remove browser default top margin
margin-top: 0;
// Reset browser default of `1em` to use `em`s
margin-bottom: 1em;
// Don't allow content to break outside
overflow: auto;
}
//
// Figures
//
figure {
// Apply a consistent margin strategy (matches our type styles).
margin: 0 0 1em;
}
//
// Images and content
//
img {
vertical-align: middle;
border-style: none; // remove the border on images inside links in IE 10-.
}
// Avoid 300ms click delay on touch devices that support the `touch-action` CSS property.
//
// In particular, unlike most other browsers, IE11+Edge on Windows 10 on touch devices and IE Mobile 10-11
// DON'T remove the click delay when `<meta name="viewport" content="width=device-width">` is present.
// However, they DO support emoving the click delay via `touch-action: manipulation`.
// See:
// * https://getbootstrap.com/docs/4.0/content/reboot/#click-delay-optimization-for-touch
// * http://caniuse.com/#feat=css-touch-action
// * https://patrickhlauke.github.io/touch/tests/results/#suppressing-300ms-delay
a,
area,
button,
[role='button'],
input:not([type='range']),
label,
select,
summary,
textarea {
touch-action: manipulation;
}
//
// Tables
//
table {
border-collapse: collapse; // Prevent double borders
}
caption {
padding-top: 0.75em;
padding-bottom: 0.3em;
color: @text-color-secondary;
text-align: left;
caption-side: bottom;
}
//
// Forms
//
input,
button,
select,
optgroup,
textarea {
margin: 0; // remove the margin in Firefox and Safari
color: inherit;
font-size: inherit;
font-family: inherit;
line-height: inherit;
}
button,
input {
overflow: visible; // Show the overflow in Edge
}
button,
select {
text-transform: none; // remove the inheritance of text transform in Firefox
}
// 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
// controls in Android 4.
// 2. Correct the inability to style clickable types in iOS and Safari.
button,
@{html-selector} [type="button"], /* 1 */
[type="reset"],
[type="submit"] {
-webkit-appearance: button; // 2
}
// remove inner border and padding from Firefox, but don't restore the outline like Normalize.
button::-moz-focus-inner,
[type='button']::-moz-focus-inner,
[type='reset']::-moz-focus-inner,
[type='submit']::-moz-focus-inner {
padding: 0;
border-style: none;
}
input[type='radio'],
input[type='checkbox'] {
box-sizing: border-box; // 1. Add the correct box sizing in IE 10-
padding: 0; // 2. remove the padding in IE 10-
}
input[type='date'],
input[type='time'],
input[type='datetime-local'],
input[type='month'] {
// remove the default appearance of temporal inputs to avoid a Mobile Safari
// bug where setting a custom line-height prevents text from being vertically
// centered within the input.
// See https://bugs.webkit.org/show_bug.cgi?id=139848
// and https://github.com/twbs/bootstrap/issues/11266
-webkit-appearance: listbox;
}
textarea {
overflow: auto; // remove the default vertical scrollbar in IE.
// Textareas should really only resize vertically so they don't break their (horizontal) containers.
resize: vertical;
}
fieldset {
// Browsers set a default `min-width: min-content;` on fieldsets,
// unlike e.g. `<div>`s, which have `min-width: 0;` by default.
// So we reset that to ensure fieldsets behave more like a standard block element.
// See https://github.com/twbs/bootstrap/issues/12359
// and https://html.spec.whatwg.org/multipage/#the-fieldset-and-legend-elements
min-width: 0;
margin: 0;
// Reset the default outline behavior of fieldsets so they don't affect page layout.
padding: 0;
border: 0;
}
// 1. Correct the text wrapping in Edge and IE.
// 2. Correct the color inheritance from `fieldset` elements in IE.
legend {
display: block;
width: 100%;
max-width: 100%; // 1
margin-bottom: 0.5em;
padding: 0;
color: inherit; // 2
font-size: 1.5em;
line-height: inherit;
white-space: normal; // 1
}
progress {
vertical-align: baseline; // Add the correct vertical alignment in Chrome, Firefox, and Opera.
}
// Correct the cursor style of incement and decement buttons in Chrome.
[type='number']::-webkit-inner-spin-button,
[type='number']::-webkit-outer-spin-button {
height: auto;
}
[type='search'] {
// This overrides the extra rounded corners on search inputs in iOS so that our
// `.form-control` class can properly style them. Note that this cannot simply
// be added to `.form-control` as it's not specific enough. For details, see
// https://github.com/twbs/bootstrap/issues/11586.
outline-offset: -2px; // 2. Correct the outline style in Safari.
-webkit-appearance: none;
}
//
// remove the inner padding and cancel buttons in Chrome and Safari on macOS.
//
[type='search']::-webkit-search-cancel-button,
[type='search']::-webkit-search-decoration {
-webkit-appearance: none;
}
//
// 1. Correct the inability to style clickable types in iOS and Safari.
// 2. Change font properties to `inherit` in Safari.
//
::-webkit-file-upload-button {
font: inherit; // 2
-webkit-appearance: button; // 1
}
//
// Correct element displays
//
output {
display: inline-block;
}
summary {
display: list-item; // Add the correct display in all browsers
}
template {
display: none; // Add the correct display in IE
}
// Always hide an element with the `hidden` HTML attribute (from PureCSS).
// Needed for proper display in IE 10-.
[hidden] {
display: none !important;
}
mark {
padding: 0.2em;
background-color: @yellow-1;
}
::selection {
color: @text-color-inverse;
background: @text-selection-bg;
}
// Utility classes
.clearfix {
.clearfix();
}

View File

@@ -0,0 +1,22 @@
@import '../themes/index';
@import '../mixins/iconfont';
.@{iconfont-css-prefix} {
.iconfont-mixin();
// https://github.com/ant-design/ant-design/issues/33703
& > & {
line-height: 0;
vertical-align: 0;
}
&[tabindex] {
cursor: pointer;
}
}
.@{iconfont-css-prefix}-spin,
.@{iconfont-css-prefix}-spin::before {
display: inline-block;
animation: loadingCircle 1s infinite linear;
}

View File

@@ -0,0 +1,5 @@
@import '../mixins/index';
@import 'base';
@import 'global';
@import 'iconfont';
@import 'motion';

View File

@@ -0,0 +1,22 @@
// @import '../mixins/motion'; This has moved to theme/xxx inside.
@import 'motion/fade';
@import 'motion/move';
@import 'motion/other';
@import 'motion/slide';
@import 'motion/zoom';
// For common/openAnimation
.ant-motion-collapse-legacy {
overflow: hidden;
&-active {
transition: height @animation-duration-base @ease-in-out,
opacity @animation-duration-base @ease-in-out !important;
}
}
.ant-motion-collapse {
overflow: hidden;
transition: height @animation-duration-base @ease-in-out,
opacity @animation-duration-base @ease-in-out !important;
}

View File

@@ -0,0 +1,34 @@
.fade-motion(@className, @keyframeName) {
@name: ~'@{ant-prefix}-@{className}';
.make-motion(@name, @keyframeName);
.@{name}-enter,
.@{name}-appear {
opacity: 0;
animation-timing-function: linear;
}
.@{name}-leave {
animation-timing-function: linear;
}
}
.fade-motion(fade, antFade);
@keyframes antFadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes antFadeOut {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}

View File

@@ -0,0 +1,129 @@
.move-motion(@className, @keyframeName) {
@name: ~'@{ant-prefix}-@{className}';
.make-motion(@name, @keyframeName);
.@{name}-enter,
.@{name}-appear {
opacity: 0;
animation-timing-function: @ease-out-circ;
}
.@{name}-leave {
animation-timing-function: @ease-in-circ;
}
}
.move-motion(move-up, antMoveUp);
.move-motion(move-down, antMoveDown);
.move-motion(move-left, antMoveLeft);
.move-motion(move-right, antMoveRight);
@keyframes antMoveDownIn {
0% {
transform: translateY(100%);
transform-origin: 0 0;
opacity: 0;
}
100% {
transform: translateY(0%);
transform-origin: 0 0;
opacity: 1;
}
}
@keyframes antMoveDownOut {
0% {
transform: translateY(0%);
transform-origin: 0 0;
opacity: 1;
}
100% {
transform: translateY(100%);
transform-origin: 0 0;
opacity: 0;
}
}
@keyframes antMoveLeftIn {
0% {
transform: translateX(-100%);
transform-origin: 0 0;
opacity: 0;
}
100% {
transform: translateX(0%);
transform-origin: 0 0;
opacity: 1;
}
}
@keyframes antMoveLeftOut {
0% {
transform: translateX(0%);
transform-origin: 0 0;
opacity: 1;
}
100% {
transform: translateX(-100%);
transform-origin: 0 0;
opacity: 0;
}
}
@keyframes antMoveRightIn {
0% {
transform: translateX(100%);
transform-origin: 0 0;
opacity: 0;
}
100% {
transform: translateX(0%);
transform-origin: 0 0;
opacity: 1;
}
}
@keyframes antMoveRightOut {
0% {
transform: translateX(0%);
transform-origin: 0 0;
opacity: 1;
}
100% {
transform: translateX(100%);
transform-origin: 0 0;
opacity: 0;
}
}
@keyframes antMoveUpIn {
0% {
transform: translateY(-100%);
transform-origin: 0 0;
opacity: 0;
}
100% {
transform: translateY(0%);
transform-origin: 0 0;
opacity: 1;
}
}
@keyframes antMoveUpOut {
0% {
transform: translateY(0%);
transform-origin: 0 0;
opacity: 1;
}
100% {
transform: translateY(-100%);
transform-origin: 0 0;
opacity: 0;
}
}

View File

@@ -0,0 +1,51 @@
@keyframes loadingCircle {
100% {
transform: rotate(360deg);
}
}
@click-animating-true: ~"[@{ant-prefix}-click-animating='true']";
@click-animating-with-extra-node-true: ~"[@{ant-prefix}-click-animating-without-extra-node='true']";
@{click-animating-true},
@{click-animating-with-extra-node-true} {
position: relative;
}
html {
--antd-wave-shadow-color: @primary-color;
--scroll-bar: 0;
}
@click-animating-with-extra-node-true-after: ~'@{click-animating-with-extra-node-true}::after';
@{click-animating-with-extra-node-true-after},
.@{ant-prefix}-click-animating-node {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
display: block;
border-radius: inherit;
box-shadow: 0 0 0 0 @primary-color;
box-shadow: 0 0 0 0 var(--antd-wave-shadow-color);
opacity: 0.2;
animation: fadeEffect 2s @ease-out-circ, waveEffect 0.4s @ease-out-circ;
animation-fill-mode: forwards;
content: '';
pointer-events: none;
}
@keyframes waveEffect {
100% {
box-shadow: 0 0 0 @primary-color;
box-shadow: 0 0 0 @wave-animation-width var(--antd-wave-shadow-color);
}
}
@keyframes fadeEffect {
100% {
opacity: 0;
}
}

View File

@@ -0,0 +1,131 @@
.slide-motion(@className, @keyframeName) {
@name: ~'@{ant-prefix}-@{className}';
.make-motion(@name, @keyframeName);
.@{name}-enter,
.@{name}-appear {
transform: scale(0);
transform-origin: 0% 0%;
opacity: 0;
animation-timing-function: @ease-out-quint;
}
.@{name}-leave {
animation-timing-function: @ease-in-quint;
}
}
.slide-motion(slide-up, antSlideUp);
.slide-motion(slide-down, antSlideDown);
.slide-motion(slide-left, antSlideLeft);
.slide-motion(slide-right, antSlideRight);
@keyframes antSlideUpIn {
0% {
transform: scaleY(0.8);
transform-origin: 0% 0%;
opacity: 0;
}
100% {
transform: scaleY(1);
transform-origin: 0% 0%;
opacity: 1;
}
}
@keyframes antSlideUpOut {
0% {
transform: scaleY(1);
transform-origin: 0% 0%;
opacity: 1;
}
100% {
transform: scaleY(0.8);
transform-origin: 0% 0%;
opacity: 0;
}
}
@keyframes antSlideDownIn {
0% {
transform: scaleY(0.8);
transform-origin: 100% 100%;
opacity: 0;
}
100% {
transform: scaleY(1);
transform-origin: 100% 100%;
opacity: 1;
}
}
@keyframes antSlideDownOut {
0% {
transform: scaleY(1);
transform-origin: 100% 100%;
opacity: 1;
}
100% {
transform: scaleY(0.8);
transform-origin: 100% 100%;
opacity: 0;
}
}
@keyframes antSlideLeftIn {
0% {
transform: scaleX(0.8);
transform-origin: 0% 0%;
opacity: 0;
}
100% {
transform: scaleX(1);
transform-origin: 0% 0%;
opacity: 1;
}
}
@keyframes antSlideLeftOut {
0% {
transform: scaleX(1);
transform-origin: 0% 0%;
opacity: 1;
}
100% {
transform: scaleX(0.8);
transform-origin: 0% 0%;
opacity: 0;
}
}
@keyframes antSlideRightIn {
0% {
transform: scaleX(0.8);
transform-origin: 100% 0%;
opacity: 0;
}
100% {
transform: scaleX(1);
transform-origin: 100% 0%;
opacity: 1;
}
}
@keyframes antSlideRightOut {
0% {
transform: scaleX(1);
transform-origin: 100% 0%;
opacity: 1;
}
100% {
transform: scaleX(0.8);
transform-origin: 100% 0%;
opacity: 0;
}
}

View File

@@ -0,0 +1,179 @@
.zoom-motion(@className, @keyframeName, @duration: @animation-duration-base) {
@name: ~'@{ant-prefix}-@{className}';
.make-motion(@name, @keyframeName, @duration);
.@{name}-enter,
.@{name}-appear {
transform: scale(0); // need this by yiminghe
opacity: 0;
animation-timing-function: @ease-out-circ;
&-prepare {
transform: none;
}
}
.@{name}-leave {
animation-timing-function: @ease-in-out-circ;
}
}
// For Modal, Select choosen item
.zoom-motion(zoom, antZoom);
// For Popover, Popconfirm, Dropdown
.zoom-motion(zoom-big, antZoomBig);
// For Tooltip
.zoom-motion(zoom-big-fast, antZoomBig, @animation-duration-fast);
.zoom-motion(zoom-up, antZoomUp);
.zoom-motion(zoom-down, antZoomDown);
.zoom-motion(zoom-left, antZoomLeft);
.zoom-motion(zoom-right, antZoomRight);
@keyframes antZoomIn {
0% {
transform: scale(0.2);
opacity: 0;
}
100% {
transform: scale(1);
opacity: 1;
}
}
@keyframes antZoomOut {
0% {
transform: scale(1);
}
100% {
transform: scale(0.2);
opacity: 0;
}
}
@keyframes antZoomBigIn {
0% {
transform: scale(0.8);
opacity: 0;
}
100% {
transform: scale(1);
opacity: 1;
}
}
@keyframes antZoomBigOut {
0% {
transform: scale(1);
}
100% {
transform: scale(0.8);
opacity: 0;
}
}
@keyframes antZoomUpIn {
0% {
transform: scale(0.8);
transform-origin: 50% 0%;
opacity: 0;
}
100% {
transform: scale(1);
transform-origin: 50% 0%;
}
}
@keyframes antZoomUpOut {
0% {
transform: scale(1);
transform-origin: 50% 0%;
}
100% {
transform: scale(0.8);
transform-origin: 50% 0%;
opacity: 0;
}
}
@keyframes antZoomLeftIn {
0% {
transform: scale(0.8);
transform-origin: 0% 50%;
opacity: 0;
}
100% {
transform: scale(1);
transform-origin: 0% 50%;
}
}
@keyframes antZoomLeftOut {
0% {
transform: scale(1);
transform-origin: 0% 50%;
}
100% {
transform: scale(0.8);
transform-origin: 0% 50%;
opacity: 0;
}
}
@keyframes antZoomRightIn {
0% {
transform: scale(0.8);
transform-origin: 100% 50%;
opacity: 0;
}
100% {
transform: scale(1);
transform-origin: 100% 50%;
}
}
@keyframes antZoomRightOut {
0% {
transform: scale(1);
transform-origin: 100% 50%;
}
100% {
transform: scale(0.8);
transform-origin: 100% 50%;
opacity: 0;
}
}
@keyframes antZoomDownIn {
0% {
transform: scale(0.8);
transform-origin: 50% 100%;
opacity: 0;
}
100% {
transform: scale(1);
transform-origin: 50% 100%;
}
}
@keyframes antZoomDownOut {
0% {
transform: scale(1);
transform-origin: 50% 100%;
}
100% {
transform: scale(0.8);
transform-origin: 50% 100%;
opacity: 0;
}
}

View File

@@ -0,0 +1 @@
import './index.css';

View File

@@ -0,0 +1,4 @@
@root-entry-name: default;
@import './themes/dark.less';
@import './core/index';

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,4 @@
// This is same as `index.less` but given `root-entry-name` for `dist/antd.less` usage
@root-entry-name: default;
@import './index';

View File

@@ -0,0 +1,2 @@
@import './themes/index';
@import './core/index';

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
import './index.less';

View File

@@ -0,0 +1 @@
import './index.less';

View File

@@ -0,0 +1,3 @@
@root-entry-name: default;
@import './index-pure.less';

View File

@@ -0,0 +1,7 @@
.box(@position: absolute) {
position: @position;
top: 0;
right: 0;
bottom: 0;
left: 0;
}

View File

@@ -0,0 +1,16 @@
// mixins for clearfix
// ------------------------
.clearfix() {
// https://github.com/ant-design/ant-design/issues/21301#issuecomment-583955229
&::before {
display: table;
content: '';
}
&::after {
// https://github.com/ant-design/ant-design/issues/21864
display: table;
clear: both;
content: '';
}
}

View File

@@ -0,0 +1,41 @@
.compact-item-vertical-border-radius(@prefix-cls) {
&-item:not(&-first-item):not(&-last-item) {
border-radius: 0;
}
&-item&-first-item:not(&-last-item) {
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
&-item&-last-item:not(&-first-item) {
border-top-left-radius: 0;
border-top-right-radius: 0;
}
}
.compact-item-vertical-border(@prefix-cls) {
// border collapse
&-item:not(&-last-item) {
margin-bottom: -@border-width-base;
}
&-item {
&:hover,
&:focus,
&:active {
z-index: 2;
}
&[disabled] {
z-index: 0;
}
}
}
.compact-item-vertical(@prefix-cls) {
&-compact-vertical {
.compact-item-vertical-border(@prefix-cls);
.compact-item-vertical-border-radius(@prefix-cls);
}
}

View File

@@ -0,0 +1,133 @@
.compact-item-border-radius(@prefix-cls, @bordered-item-cls: null) {
& when (@bordered-item-cls = null) {
// border-radius
&-item:not(&-first-item):not(&-last-item).@{prefix-cls} {
border-radius: 0;
}
&-item.@{prefix-cls}&-first-item:not(&-last-item):not(&-item-rtl) {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
&-item.@{prefix-cls}&-last-item:not(&-first-item):not(&-item-rtl) {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
// ----------rtl for first item----------
&-item.@{prefix-cls}&-item-rtl&-first-item:not(&-last-item) {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
// ----------rtl for last item----------
&-item.@{prefix-cls}&-item-rtl&-last-item:not(&-first-item) {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
}
& when (not (@bordered-item-cls = null)) {
// border-radius
&-item:not(&-first-item):not(&-last-item).@{prefix-cls} > .@{bordered-item-cls} {
border-radius: 0;
}
&-item&-first-item.@{prefix-cls}:not(&-last-item):not(&-item-rtl) > .@{bordered-item-cls} {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
&-item&-last-item.@{prefix-cls}:not(&-first-item):not(&-item-rtl) > .@{bordered-item-cls} {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
// ----------rtl for first item----------
&-item.@{prefix-cls}&-first-item&-item-rtl:not(&-last-item) > .@{bordered-item-cls} {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
// ----------rtl for last item----------
&-item.@{prefix-cls}&-last-item&-item-rtl:not(&-first-item) > .@{bordered-item-cls} {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
}
}
.compact-item-border(@prefix-cls, @bordered-item-cls: null, @special-open-cls) {
& when (@bordered-item-cls = null) {
// border collapse
&-item:not(&-last-item):not(&-item-rtl) {
margin-right: -@border-width-base;
}
// rtl border collapse
&-item:not(&-last-item)&-item-rtl {
margin-left: -@border-width-base;
}
&-item {
&:hover,
&:focus,
&:active {
z-index: 2;
}
// Select has an extra focus className
& when (not (@special-item-cls = null)) {
&.@{special-item-cls} {
z-index: 2;
}
}
&[disabled] {
z-index: 0;
}
}
}
& when (not (@bordered-item-cls = null)) {
// border collapse
&-item:not(&-last-item) {
margin-right: -@border-width-base;
&.@{prefix-cls}-compact-item-rtl {
margin-right: 0;
margin-left: -@border-width-base;
}
}
&-item {
&:hover,
&:focus,
&:active {
> * {
z-index: 2;
}
}
// Select has an special focus-item
& when (not (@special-item-cls = null)) {
&.@{special-item-cls} > * {
z-index: 2;
}
}
&[disabled] > * {
z-index: 0;
}
}
}
}
.compact-item(@prefix-cls, @bordered-item-cls: null, @special-item-cls: null) {
&-compact {
.compact-item-border(@prefix-cls, @bordered-item-cls, @special-item-cls);
.compact-item-border-radius(@prefix-cls, @bordered-item-cls);
}
}

View File

@@ -0,0 +1,13 @@
// Compatibility for browsers.
// Placeholder text
.placeholder(@color: @input-placeholder-color) {
&::placeholder {
color: @color;
user-select: none; // https://github.com/ant-design/ant-design/pull/32639
}
&:placeholder-shown {
text-overflow: ellipsis;
}
}

View File

@@ -0,0 +1,181 @@
// customize dark components background in popover containers(like Modal, Drawer, Card, Popover, Popconfirm, Notification, ...)
// for dark theme
.popover-customize-bg(@containerClass, @background: @popover-background, @prefix: @ant-prefix)
when
(@theme = dark) {
@picker-prefix-cls: ~'@{prefix}-picker';
@slider-prefix-cls: ~'@{prefix}-slider';
@anchor-prefix-cls: ~'@{prefix}-anchor';
@collapse-prefix-cls: ~'@{prefix}-collapse';
@tab-prefix-cls: ~'@{prefix}-tabs';
@timeline-prefix-cls: ~'@{prefix}-timeline';
@tree-prefix-cls: ~'@{prefix}-tree';
@card-prefix-cls: ~'@{prefix}-card';
@badge-prefix-cls: ~'@{prefix}-badge';
@transfer-prefix-cls: ~'@{prefix}-transfer';
@calendar-prefix-cls: ~'@{prefix}-picker-calendar';
@calendar-picker-prefix-cls: ~'@{prefix}-picker';
@table-prefix-cls: ~'@{prefix}-table';
@popover-border: @border-width-base @border-style-base @popover-customize-border-color;
.@{containerClass} {
.@{picker-prefix-cls}-clear,
.@{slider-prefix-cls}-handle,
.@{anchor-prefix-cls}-wrapper,
.@{collapse-prefix-cls}-content,
.@{timeline-prefix-cls}-item-head,
.@{card-prefix-cls} {
background-color: @background;
}
.@{transfer-prefix-cls} {
&-list {
&-header {
background: @background;
border-bottom: @popover-border;
}
&-content-item:not(.@{transfer-prefix-cls}-list-content-item-disabled):hover {
background-color: @item-hover-bg;
}
}
}
tr.@{table-prefix-cls}-expanded-row {
&,
&:hover {
> td {
background: #272727;
}
}
}
.@{table-prefix-cls}.@{table-prefix-cls}-small {
thead {
> tr {
> th {
background-color: @background;
border-bottom: @popover-border;
}
}
}
}
.@{table-prefix-cls} {
background-color: @background;
.@{table-prefix-cls}-row-expand-icon {
border: @popover-border;
}
tfoot {
> tr {
> th,
> td {
border-bottom: @popover-border;
}
}
}
thead {
> tr {
> th {
background-color: #272727;
border-bottom: @popover-border;
}
}
}
tbody {
> tr {
> td {
border-bottom: @popover-border;
&.@{table-prefix-cls}-cell-fix-left,
&.@{table-prefix-cls}-cell-fix-right {
background-color: @background;
}
}
&.@{table-prefix-cls}-row:hover {
> td {
background: @table-header-sort-active-bg;
}
}
}
}
&.@{table-prefix-cls}-bordered {
.@{table-prefix-cls}-title {
border: @popover-border;
}
// ============================= Cell =============================
thead > tr > th,
tbody > tr > td,
tfoot > tr > th,
tfoot > tr > td {
border-right: @popover-border;
}
// Fixed right should provides additional border
.@{table-prefix-cls}-cell-fix-right-first::after {
border-right: @popover-border;
}
// ============================ Header ============================
table > {
thead {
> tr:not(:last-child) > th {
border-bottom: @border-width-base @border-style-base @border-color-split;
}
}
}
// =========================== Content ============================
.@{table-prefix-cls}-container {
border: @popover-border;
}
// ========================== Expandable ==========================
.@{table-prefix-cls}-expanded-row-fixed {
&::after {
border-right: @popover-border;
}
}
.@{table-prefix-cls}-footer {
border: @popover-border;
}
}
.@{table-prefix-cls}-filter-trigger-container-open {
background-color: #525252;
}
}
.@{calendar-prefix-cls}-full {
background-color: @background;
.@{calendar-picker-prefix-cls}-panel {
background-color: @background;
.@{calendar-prefix-cls}-date {
border-top: 2px solid @popover-customize-border-color;
}
}
}
.@{tab-prefix-cls} {
&.@{tab-prefix-cls}-card .@{tab-prefix-cls}-card-bar .@{tab-prefix-cls}-tab-active {
background-color: @background;
border-bottom: @border-width-base solid @background;
}
}
.@{badge-prefix-cls} {
&-count {
box-shadow: 0 0 0 1px @background;
}
}
.@{tree-prefix-cls} {
&-show-line {
.@{tree-prefix-cls}-switcher {
background: @background;
}
}
}
}
}

View File

@@ -0,0 +1,29 @@
.iconfont-mixin() {
display: inline-flex;
align-items: center;
color: @icon-color;
font-style: normal;
line-height: 0;
text-align: center;
text-transform: none;
vertical-align: -0.125em; // for SVG icon, see https://blog.prototypr.io/align-svg-icons-to-text-and-say-goodbye-to-font-icons-d44b3d7b26b4
text-rendering: optimizelegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
> * {
line-height: 1;
}
svg {
display: inline-block;
}
&::before {
display: none; // dont display old icon.
}
& &-icon {
display: block;
}
}

View File

@@ -0,0 +1,16 @@
// Mixins
// --------------------------------------------------
@import 'size';
@import 'compatibility';
@import 'clearfix';
@import 'iconfont';
@import 'typography';
@import 'customize';
@import 'box';
@import 'modal-mask';
@import 'motion';
@import 'reset';
@import 'operation-unit';
@import 'rounded-arrow';
@import 'compact-item';
@import 'compact-item-vertical';

View File

@@ -0,0 +1,30 @@
@import 'box';
.modal-mask() {
pointer-events: none;
&.@{ant-prefix}-zoom-enter,
&.@{ant-prefix}-zoom-appear {
transform: none; // reset scale avoid mousePosition bug
opacity: 0;
animation-duration: @animation-duration-slow;
user-select: none; // https://github.com/ant-design/ant-design/issues/11777
}
&-mask {
.box(fixed);
z-index: @zindex-modal-mask;
height: 100%;
background-color: @modal-mask-bg;
&-hidden {
display: none;
}
}
&-wrap {
.box(fixed);
overflow: auto;
outline: 0;
}
}

View File

@@ -0,0 +1,33 @@
.motion-common(@duration: @animation-duration-base) {
animation-duration: @duration;
animation-fill-mode: both;
}
.motion-common-leave(@duration: @animation-duration-base) {
animation-duration: @duration;
animation-fill-mode: both;
}
.make-motion(@className, @keyframeName, @duration: @animation-duration-base) {
.@{className}-enter,
.@{className}-appear {
.motion-common(@duration);
animation-play-state: paused;
}
.@{className}-leave {
.motion-common-leave(@duration);
animation-play-state: paused;
}
.@{className}-enter.@{className}-enter-active,
.@{className}-appear.@{className}-appear-active {
animation-name: ~'@{keyframeName}In';
animation-play-state: running;
}
.@{className}-leave.@{className}-leave-active {
animation-name: ~'@{keyframeName}Out';
animation-play-state: running;
pointer-events: none;
}
}

View File

@@ -0,0 +1,15 @@
.operation-unit() {
color: @link-color;
outline: none;
cursor: pointer;
transition: color 0.3s;
&:focus-visible,
&:hover {
color: @link-hover-color;
}
&:active {
color: @link-active-color;
}
}

View File

@@ -0,0 +1,11 @@
.reset-component() {
box-sizing: border-box;
margin: 0;
padding: 0;
color: @text-color;
font-size: @font-size-base;
font-variant: @font-variant-base;
line-height: @line-height-base;
list-style: none;
font-feature-settings: @font-feature-settings-base;
}

View File

@@ -0,0 +1,44 @@
.roundedArrow(@width, @outer-radius, @bg-color: var(--antd-arrow-background-color)) {
@corner-height: unit(((@outer-radius) * (1 - 1 / sqrt(2))));
@width-without-unit: unit(@width);
@outer-radius-without-unit: unit(@outer-radius);
@inner-radius-without-unit: unit(@arrow-border-radius);
@a-x: @width-without-unit - @corner-height;
@a-y: 2 * @width-without-unit + @corner-height;
@b-x: @a-x + @outer-radius-without-unit * (1 / sqrt(2));
@b-y: 2 * @width-without-unit;
@c-x: 2 * @width-without-unit - @inner-radius-without-unit;
@c-y: 2 * @width-without-unit;
@d-x: 2 * @width-without-unit;
@d-y: 2 * @width-without-unit - @inner-radius-without-unit;
@e-x: 2 * @width-without-unit;
@e-y: @f-y + @outer-radius-without-unit * (1 / sqrt(2));
@f-x: 2 * @width-without-unit + @corner-height;
@f-y: @width-without-unit - @corner-height;
@g-x: @f-x - 1;
@g-y: @f-y;
@h-x: @a-x;
@h-y: @a-y - 1;
border-radius: 0 0 @arrow-border-radius;
pointer-events: none;
&::before {
position: absolute;
top: -@width;
left: -@width;
width: @width * 3;
height: @width * 3;
background: @bg-color;
// Hack firefox: https://github.com/ant-design/ant-design/pull/33710#issuecomment-1015287825
background-repeat: no-repeat;
background-position: ceil(-@width + 1px) ceil(-@width + 1px);
content: '';
clip-path: inset(33% 33%); // For browsers that do not support path()
clip-path: path(
'M @{a-x} @{a-y} A @{outer-radius-without-unit} @{outer-radius-without-unit} 0 0 1 @{b-x} @{b-y} L @{c-x} @{c-y} A @{inner-radius-without-unit} @{inner-radius-without-unit} 0 0 0 @{d-x} @{d-y} L @{e-x} @{e-y} A @{outer-radius-without-unit} @{outer-radius-without-unit} 0 0 1 @{f-x} @{f-y} L @{g-x} @{g-y} L @{h-x} @{h-y} Z'
);
}
}

View File

@@ -0,0 +1,10 @@
// Sizing shortcuts
.size(@width; @height) {
width: @width;
height: @height;
}
.square(@size) {
.size(@size; @size);
}

View File

@@ -0,0 +1,58 @@
// =============== Common ===============
.typography-paragraph() {
margin-bottom: 1em;
}
.typography-title(@fontSize; @fontWeight; @lineHeight; @headingColor; @headingMarginBottom;) {
margin-bottom: @headingMarginBottom;
color: @headingColor;
font-weight: @fontWeight;
font-size: @fontSize;
line-height: @lineHeight;
}
.typography-title-1() {
.typography-title(
@heading-1-size,
@typography-title-font-weight,
1.23,
@heading-color,
@typography-title-margin-bottom
);
}
.typography-title-2() {
.typography-title(
@heading-2-size,
@typography-title-font-weight,
1.35,
@heading-color,
@typography-title-margin-bottom
);
}
.typography-title-3() {
.typography-title(
@heading-3-size,
@typography-title-font-weight,
1.35,
@heading-color,
@typography-title-margin-bottom
);
}
.typography-title-4() {
.typography-title(
@heading-4-size,
@typography-title-font-weight,
1.4,
@heading-color,
@typography-title-margin-bottom
);
}
.typography-title-5() {
.typography-title(
@heading-5-size,
@typography-title-font-weight,
1.5,
@heading-color,
@typography-title-margin-bottom
);
}

View File

@@ -0,0 +1,295 @@
@import './default.less';
@line-height-base: 1.66667;
@mode: compact;
@font-size-base: 12px;
@font-size-lg: @font-size-base + 2px;
// default paddings
@default-padding-lg: 24px; // containers
@default-padding-md: 16px; // small containers and buttons
@default-padding-sm: 12px; // Form controls and items
@default-padding-xs: 8px; // small items
@default-padding-xss: 4px; // more small
// vertical paddings
@padding-lg: 16px; // containers
@padding-md: 8px; // small containers and buttons
@padding-sm: 8px; // Form controls and items
@padding-xs: 4px; // small items
@padding-xss: 0px; // more small
// vertical padding for all form controls
@control-padding-horizontal: @padding-sm;
@control-padding-horizontal-sm: @default-padding-xs;
// vertical margins
@margin-lg: 16px; // containers
@margin-md: 8px; // small containers and buttons
@margin-sm: 8px; // Form controls and items
@margin-xs: 4px; // small items
@margin-xss: 0px; // more small
// height rules
@height-base: 28px;
@height-lg: 32px;
@height-sm: 22px;
// Button
// ---
@btn-padding-horizontal-base: @default-padding-sm - 1px;
@btn-padding-horizontal-lg: @btn-padding-horizontal-base;
@btn-padding-horizontal-sm: @default-padding-xs - 1px;
@btn-square-only-icon-size-lg: 16px;
@btn-square-only-icon-size: 14px;
@btn-square-only-icon-size-sm: 12px;
// Breadcrumb
// ---
@breadcrumb-font-size: @font-size-base;
@breadcrumb-icon-font-size: @font-size-base;
//Dropdown
@dropdown-line-height: 18px;
// Menu
@menu-item-padding-horizontal: 12px;
@menu-horizontal-line-height: 38px;
@menu-inline-toplevel-item-height: 32px;
@menu-item-height: 32px;
@menu-item-vertical-margin: 0px;
@menu-item-boundary-margin: 0px;
@menu-icon-margin-right: 8px;
// Checkbox
@checkbox-size: 14px;
@checkbox-group-item-margin-right: 6px;
// picker
@picker-panel-cell-height: 22px;
@picker-panel-cell-width: 32px;
@picker-text-height: 32px;
@picker-time-panel-cell-height: 24px;
@picker-panel-without-time-cell-height: 48px;
// Form
// ---
@form-item-margin-bottom: 16px;
@form-vertical-label-padding: 0 0 4px;
// Rate
// ---
@rate-star-size: 16px;
// Radio
// ---
@radio-size: 14px;
@radio-wrapper-margin-right: 6px;
// Switch
// ---
@switch-height: 20px;
@switch-sm-height: 14px;
@switch-min-width: 40px;
@switch-sm-min-width: 24px;
@switch-inner-margin-min: 4px;
@switch-inner-margin-max: 22px;
// Slider
// ---
@slider-handle-size: 12px;
@slider-handle-margin-top: -4px;
// Input
// ---
@input-padding-vertical-base: round(
max(
(round(((@input-height-base - @font-size-base * @line-height-base) / 2) * 10) / 10) -
@border-width-base,
2px
)
);
@input-padding-horizontal-lg: 11px;
// PageHeader
// ---
@page-header-padding: 16px;
@page-header-padding-vertical: 8px;
@page-header-heading-title: 16px;
@page-header-heading-sub-title: 12px;
@page-header-tabs-tab-font-size: 14px;
// Pagination
// ---
@pagination-mini-options-size-changer-top: 1px;
@pagination-item-size-sm: 22px;
// Cascader
// ----
@cascader-dropdown-line-height: @dropdown-line-height;
// Select
// ---
@select-dropdown-height: @height-base;
@select-single-item-height-lg: 32px;
@select-multiple-item-height: @input-height-base - max(@input-padding-vertical-base, 4) * 2; // Normal 24px
@select-multiple-item-height-lg: 24px;
@select-multiple-item-spacing-half: 3px;
// Tree
// ---
@tree-title-height: 20px;
// Transfer
// ---
@transfer-item-padding-vertical: 3px;
@transfer-list-search-icon-top: 8px;
@transfer-header-height: 36px;
// Comment
// ---
@comment-actions-margin-bottom: 0px;
@comment-actions-margin-top: @margin-xs;
@comment-content-detail-p-margin-bottom: 0px;
// Steps
// ---
@steps-icon-size: 24px;
@steps-icon-custom-size: 20px;
@steps-icon-custom-font-size: 20px;
@steps-icon-custom-top: 2px;
@steps-icon-margin: 2px 8px 2px 0;
@steps-icon-font-size: @font-size-base;
@steps-dot-top: 4px;
@steps-icon-top: 0px;
@steps-small-icon-size: 20px;
@steps-vertical-icon-width: 12px;
@steps-vertical-tail-width: 12px;
@steps-vertical-tail-width-sm: 10px;
// Collapse
// ---
//@collapse-header-padding-extra: 32px;
@collapse-content-padding: @padding-md @padding-lg;
// List
// ---
@list-item-meta-description-font-size: @font-size-sm;
@list-item-padding-sm: 4px 12px;
@list-item-padding-lg: 12px 16px;
// Drawer
// ---
@drawer-header-padding: 11px @padding-lg;
@drawer-footer-padding-vertical: @padding-sm;
@drawer-footer-padding-horizontal: @padding-sm;
@drawer-header-close-size: 44px;
// Modal
// --
@modal-header-padding-vertical: 11px;
@modal-header-padding: @modal-header-padding-vertical @modal-header-padding-horizontal;
@modal-footer-padding-vertical: @padding-sm;
@modal-header-close-size: @modal-header-title-line-height + 2 * @modal-header-padding-vertical;
@modal-confirm-body-padding: 24px 24px 16px;
// Message
// ---
@message-notice-content-padding: 8px 16px;
// popover
// --
@popover-min-height: 28px;
@popover-padding-horizontal: @default-padding-sm;
// Card
// ---
@card-head-height: 36px;
@card-head-font-size: @card-head-font-size-sm;
@card-head-padding: 8.5px;
@card-padding-base: 12px;
@card-padding-base-sm: @card-padding-base;
@card-head-height-sm: 30px;
@card-head-padding-sm: 6px;
@card-actions-li-margin: 4px 0;
@card-head-tabs-margin-bottom: -9px;
// Table
// ---
@table-padding-vertical: 12px;
@table-padding-horizontal: 8px;
@table-padding-vertical-md: 8px;
@table-padding-horizontal-md: 8px;
@table-padding-vertical-sm: 4px;
@table-padding-horizontal-sm: 4px;
@table-selection-column-width: 32px;
// Statistic
// ---
@statistic-content-font-size: 20px;
// Alert
// ---
@alert-with-description-no-icon-padding-vertical: 7px;
@alert-with-description-padding-vertical: 11px;
@alert-icon-top: 7px + @font-size-base * (@line-height-base / 2) - (@font-size-base / 2);
@alert-with-description-icon-size: 20px;
// Skeleton
// ---
@skeleton-paragraph-margin-top: 20px;
@skeleton-paragraph-li-margin-top: 12px;
@skeleton-paragraph-li-height: 14px;
@skeleton-title-height: 14px;
@skeleton-title-paragraph-margin-top: 20px;
// Descriptions
@descriptions-title-margin-bottom: 8px;
@descriptions-default-padding: 12px @padding-lg;
@descriptions-item-padding-bottom: @padding-xs;
// Avatar
// ---
@avatar-size-base: 28px;
@avatar-size-lg: 32px;
@avatar-size-sm: 22px;
@avatar-font-size-base: 16px;
@avatar-font-size-lg: 20px;
@avatar-font-size-sm: 12px;
// Badge
// ---
@badge-height: 18px;
// Tag
// ---
@tag-line-height: 18px;
// Notification
// ---
@notification-padding-vertical: 12px;
@notification-padding-horizontal: 16px;
// Result
// ---
@result-title-font-size: 20px;
@result-icon-font-size: 64px;
@result-extra-margin: 24px 0 0 0;
// Anchor
// ---
@anchor-link-top: 4px;
@anchor-link-left: 16px;
@anchor-link-padding: @anchor-link-top 0 @anchor-link-top @anchor-link-left;
// Tabs
// ---
@tabs-card-horizontal-padding: 4px @padding-md;
// Progress
// ---
@progress-circle-text-font-size: 0.833333em;
// Image
// ---
@image-size-base: 48px;
@image-font-size-base: 24px;

View File

@@ -0,0 +1,457 @@
@import './default.less';
@theme: dark;
// color palettes
@blue-1: mix(color(~`colorPalette('@{blue-base}', 8) `), @component-background, 15%);
@blue-2: mix(color(~`colorPalette('@{blue-base}', 7) `), @component-background, 25%);
@blue-3: mix(@blue-base, @component-background, 30%);
@blue-4: mix(@blue-base, @component-background, 45%);
@blue-5: mix(@blue-base, @component-background, 65%);
@blue-6: mix(@blue-base, @component-background, 85%);
@blue-7: mix(color(~`colorPalette('@{blue-base}', 5) `), @component-background, 90%);
@blue-8: mix(color(~`colorPalette('@{blue-base}', 4) `), @component-background, 95%);
@blue-9: mix(color(~`colorPalette('@{blue-base}', 3) `), @component-background, 97%);
@blue-10: mix(color(~`colorPalette('@{blue-base}', 2) `), @component-background, 98%);
@purple-1: mix(color(~`colorPalette('@{purple-base}', 8) `), @component-background, 15%);
@purple-2: mix(color(~`colorPalette('@{purple-base}', 7) `), @component-background, 25%);
@purple-3: mix(@purple-base, @component-background, 30%);
@purple-4: mix(@purple-base, @component-background, 45%);
@purple-5: mix(@purple-base, @component-background, 65%);
@purple-6: mix(@purple-base, @component-background, 85%);
@purple-7: mix(color(~`colorPalette('@{purple-base}', 5) `), @component-background, 90%);
@purple-8: mix(color(~`colorPalette('@{purple-base}', 4) `), @component-background, 95%);
@purple-9: mix(color(~`colorPalette('@{purple-base}', 3) `), @component-background, 97%);
@purple-10: mix(color(~`colorPalette('@{purple-base}', 2) `), @component-background, 98%);
@cyan-1: mix(color(~`colorPalette('@{cyan-base}', 8) `), @component-background, 15%);
@cyan-2: mix(color(~`colorPalette('@{cyan-base}', 7) `), @component-background, 25%);
@cyan-3: mix(@cyan-base, @component-background, 30%);
@cyan-4: mix(@cyan-base, @component-background, 45%);
@cyan-5: mix(@cyan-base, @component-background, 65%);
@cyan-6: mix(@cyan-base, @component-background, 85%);
@cyan-7: mix(color(~`colorPalette('@{cyan-base}', 5) `), @component-background, 90%);
@cyan-8: mix(color(~`colorPalette('@{cyan-base}', 4) `), @component-background, 95%);
@cyan-9: mix(color(~`colorPalette('@{cyan-base}', 3) `), @component-background, 97%);
@cyan-10: mix(color(~`colorPalette('@{cyan-base}', 2) `), @component-background, 98%);
@green-1: mix(color(~`colorPalette('@{green-base}', 8) `), @component-background, 15%);
@green-2: mix(color(~`colorPalette('@{green-base}', 7) `), @component-background, 25%);
@green-3: mix(@green-base, @component-background, 30%);
@green-4: mix(@green-base, @component-background, 45%);
@green-5: mix(@green-base, @component-background, 65%);
@green-6: mix(@green-base, @component-background, 85%);
@green-7: mix(color(~`colorPalette('@{green-base}', 5) `), @component-background, 90%);
@green-8: mix(color(~`colorPalette('@{green-base}', 4) `), @component-background, 95%);
@green-9: mix(color(~`colorPalette('@{green-base}', 3) `), @component-background, 97%);
@green-10: mix(color(~`colorPalette('@{green-base}', 2) `), @component-background, 98%);
@magenta-1: mix(color(~`colorPalette('@{magenta-base}', 8) `), @component-background, 15%);
@magenta-2: mix(color(~`colorPalette('@{magenta-base}', 7) `), @component-background, 25%);
@magenta-3: mix(@magenta-base, @component-background, 30%);
@magenta-4: mix(@magenta-base, @component-background, 45%);
@magenta-5: mix(@magenta-base, @component-background, 65%);
@magenta-6: mix(@magenta-base, @component-background, 85%);
@magenta-7: mix(color(~`colorPalette('@{magenta-base}', 5) `), @component-background, 90%);
@magenta-8: mix(color(~`colorPalette('@{magenta-base}', 4) `), @component-background, 95%);
@magenta-9: mix(color(~`colorPalette('@{magenta-base}', 3) `), @component-background, 97%);
@magenta-10: mix(color(~`colorPalette('@{magenta-base}', 2) `), @component-background, 98%);
@pink-1: mix(color(~`colorPalette('@{pink-base}', 8) `), @component-background, 15%);
@pink-2: mix(color(~`colorPalette('@{pink-base}', 7) `), @component-background, 25%);
@pink-3: mix(@pink-base, @component-background, 30%);
@pink-4: mix(@pink-base, @component-background, 45%);
@pink-5: mix(@pink-base, @component-background, 65%);
@pink-6: mix(@pink-base, @component-background, 85%);
@pink-7: mix(color(~`colorPalette('@{pink-base}', 5) `), @component-background, 90%);
@pink-8: mix(color(~`colorPalette('@{pink-base}', 4) `), @component-background, 95%);
@pink-9: mix(color(~`colorPalette('@{pink-base}', 3) `), @component-background, 97%);
@pink-10: mix(color(~`colorPalette('@{pink-base}', 2) `), @component-background, 98%);
@red-1: mix(color(~`colorPalette('@{red-base}', 8) `), @component-background, 15%);
@red-2: mix(color(~`colorPalette('@{red-base}', 7) `), @component-background, 25%);
@red-3: mix(@red-base, @component-background, 30%);
@red-4: mix(@red-base, @component-background, 45%);
@red-5: mix(@red-base, @component-background, 65%);
@red-6: mix(@red-base, @component-background, 85%);
@red-7: mix(color(~`colorPalette('@{red-base}', 5) `), @component-background, 90%);
@red-8: mix(color(~`colorPalette('@{red-base}', 4) `), @component-background, 95%);
@red-9: mix(color(~`colorPalette('@{red-base}', 3) `), @component-background, 97%);
@red-10: mix(color(~`colorPalette('@{red-base}', 2) `), @component-background, 98%);
@orange-1: mix(color(~`colorPalette('@{orange-base}', 8) `), @component-background, 15%);
@orange-2: mix(color(~`colorPalette('@{orange-base}', 7) `), @component-background, 25%);
@orange-3: mix(@orange-base, @component-background, 30%);
@orange-4: mix(@orange-base, @component-background, 45%);
@orange-5: mix(@orange-base, @component-background, 65%);
@orange-6: mix(@orange-base, @component-background, 85%);
@orange-7: mix(color(~`colorPalette('@{orange-base}', 5) `), @component-background, 90%);
@orange-8: mix(color(~`colorPalette('@{orange-base}', 4) `), @component-background, 95%);
@orange-9: mix(color(~`colorPalette('@{orange-base}', 3) `), @component-background, 97%);
@orange-10: mix(color(~`colorPalette('@{orange-base}', 2) `), @component-background, 98%);
@yellow-1: mix(color(~`colorPalette('@{yellow-base}', 8) `), @component-background, 15%);
@yellow-2: mix(color(~`colorPalette('@{yellow-base}', 7) `), @component-background, 25%);
@yellow-3: mix(@yellow-base, @component-background, 30%);
@yellow-4: mix(@yellow-base, @component-background, 45%);
@yellow-5: mix(@yellow-base, @component-background, 65%);
@yellow-6: mix(@yellow-base, @component-background, 85%);
@yellow-7: mix(color(~`colorPalette('@{yellow-base}', 5) `), @component-background, 90%);
@yellow-8: mix(color(~`colorPalette('@{yellow-base}', 4) `), @component-background, 95%);
@yellow-9: mix(color(~`colorPalette('@{yellow-base}', 3) `), @component-background, 97%);
@yellow-10: mix(color(~`colorPalette('@{yellow-base}', 2) `), @component-background, 98%);
@volcano-1: mix(color(~`colorPalette('@{volcano-base}', 8) `), @component-background, 15%);
@volcano-2: mix(color(~`colorPalette('@{volcano-base}', 7) `), @component-background, 25%);
@volcano-3: mix(@volcano-base, @component-background, 30%);
@volcano-4: mix(@volcano-base, @component-background, 45%);
@volcano-5: mix(@volcano-base, @component-background, 65%);
@volcano-6: mix(@volcano-base, @component-background, 85%);
@volcano-7: mix(color(~`colorPalette('@{volcano-base}', 5) `), @component-background, 90%);
@volcano-8: mix(color(~`colorPalette('@{volcano-base}', 4) `), @component-background, 95%);
@volcano-9: mix(color(~`colorPalette('@{volcano-base}', 3) `), @component-background, 97%);
@volcano-10: mix(color(~`colorPalette('@{volcano-base}', 2) `), @component-background, 98%);
@geekblue-1: mix(color(~`colorPalette('@{geekblue-base}', 8) `), @component-background, 15%);
@geekblue-2: mix(color(~`colorPalette('@{geekblue-base}', 7) `), @component-background, 25%);
@geekblue-3: mix(@geekblue-base, @component-background, 30%);
@geekblue-4: mix(@geekblue-base, @component-background, 45%);
@geekblue-5: mix(@geekblue-base, @component-background, 65%);
@geekblue-6: mix(@geekblue-base, @component-background, 85%);
@geekblue-7: mix(color(~`colorPalette('@{geekblue-base}', 5) `), @component-background, 90%);
@geekblue-8: mix(color(~`colorPalette('@{geekblue-base}', 4) `), @component-background, 95%);
@geekblue-9: mix(color(~`colorPalette('@{geekblue-base}', 3) `), @component-background, 97%);
@geekblue-10: mix(color(~`colorPalette('@{geekblue-base}', 2) `), @component-background, 98%);
@lime-1: mix(color(~`colorPalette('@{lime-base}', 8) `), @component-background, 15%);
@lime-2: mix(color(~`colorPalette('@{lime-base}', 7) `), @component-background, 25%);
@lime-3: mix(@lime-base, @component-background, 30%);
@lime-4: mix(@lime-base, @component-background, 45%);
@lime-5: mix(@lime-base, @component-background, 65%);
@lime-6: mix(@lime-base, @component-background, 85%);
@lime-7: mix(color(~`colorPalette('@{lime-base}', 5) `), @component-background, 90%);
@lime-8: mix(color(~`colorPalette('@{lime-base}', 4) `), @component-background, 95%);
@lime-9: mix(color(~`colorPalette('@{lime-base}', 3) `), @component-background, 97%);
@lime-10: mix(color(~`colorPalette('@{lime-base}', 2) `), @component-background, 98%);
@gold-1: mix(color(~`colorPalette('@{gold-base}', 8) `), @component-background, 15%);
@gold-2: mix(color(~`colorPalette('@{gold-base}', 7) `), @component-background, 25%);
@gold-3: mix(@gold-base, @component-background, 30%);
@gold-4: mix(@gold-base, @component-background, 45%);
@gold-5: mix(@gold-base, @component-background, 65%);
@gold-6: mix(@gold-base, @component-background, 85%);
@gold-7: mix(color(~`colorPalette('@{gold-base}', 5) `), @component-background, 90%);
@gold-8: mix(color(~`colorPalette('@{gold-base}', 4) `), @component-background, 95%);
@gold-9: mix(color(~`colorPalette('@{gold-base}', 3) `), @component-background, 97%);
@gold-10: mix(color(~`colorPalette('@{gold-base}', 2) `), @component-background, 98%);
// Color used by default to control hover and active backgrounds and for
// alert info backgrounds.
@primary-1: mix(color(~`colorPalette('@{primary-color}', 8) `), @component-background, 15%);
@primary-2: mix(color(~`colorPalette('@{primary-color}', 7) `), @component-background, 25%);
@primary-3: mix(@primary-color, @component-background, 30%);
@primary-4: mix(@primary-color, @component-background, 45%);
@primary-5: mix(@primary-color, @component-background, 65%);
@primary-6: @primary-color;
@primary-7: mix(color(~`colorPalette('@{primary-color}', 5) `), @component-background, 90%);
@primary-8: mix(color(~`colorPalette('@{primary-color}', 4) `), @component-background, 95%);
@primary-9: mix(color(~`colorPalette('@{primary-color}', 3) `), @component-background, 97%);
@primary-10: mix(color(~`colorPalette('@{primary-color}', 2) `), @component-background, 98%);
// Layer background
@popover-background: #1f1f1f;
@popover-customize-border-color: #3a3a3a;
@body-background: @black;
@component-background: #141414;
@text-color: fade(@white, 85%);
@text-color-secondary: fade(@white, 45%);
@text-color-inverse: @white;
@icon-color-hover: fade(@white, 75%);
@heading-color: fade(@white, 85%);
// The background colors for active and hover states for things like
// list items or table cells.
@item-active-bg: @primary-1;
@item-hover-bg: fade(@white, 8%);
// Border color
@border-color-base: #434343; // base border outline a component
@border-color-split: #303030; // split border inside a component
@background-color-light: fade(@white, 4%); // background of header and selected item
@background-color-base: fade(@white, 8%); // Default grey background color
// Disabled states
@disabled-color: fade(@white, 30%);
@disabled-bg: @background-color-base;
@disabled-color-dark: fade(@white, 30%);
// Tree
// ---
@tree-bg: transparent;
// List
// ---
@list-customize-card-bg: transparent;
// Shadow
// ---
@shadow-color: rgba(0, 0, 0, 0.45);
@shadow-color-inverse: @component-background;
@box-shadow-base: @shadow-2;
@shadow-1-up: 0 -6px 16px -8px rgba(0, 0, 0, 0.32), 0 -9px 28px 0 rgba(0, 0, 0, 0.2),
0 -12px 48px 16px rgba(0, 0, 0, 0.12);
@shadow-1-down: 0 6px 16px -8px rgba(0, 0, 0, 0.32), 0 9px 28px 0 rgba(0, 0, 0, 0.2),
0 12px 48px 16px rgba(0, 0, 0, 0.12);
@shadow-1-right: 6px 0 16px -8px rgba(0, 0, 0, 0.32), 9px 0 28px 0 rgba(0, 0, 0, 0.2),
12px 0 48px 16px rgba(0, 0, 0, 0.12);
@shadow-2: 0 3px 6px -4px rgba(0, 0, 0, 0.48), 0 6px 16px 0 rgba(0, 0, 0, 0.32),
0 9px 28px 8px rgba(0, 0, 0, 0.2);
// Buttons
// ---
@btn-shadow: 0 2px 0 rgba(0, 0, 0, 0.015);
@btn-primary-shadow: 0 2px 0 rgba(0, 0, 0, 0.045);
@btn-text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.12);
@btn-default-bg: transparent;
@btn-default-ghost-color: @text-color;
@btn-default-ghost-border: fade(@white, 25%);
@btn-text-hover-bg: rgba(255, 255, 255, 0.03);
// Checkbox
// ---
@checkbox-check-bg: transparent;
// Descriptions
// ---
@descriptions-bg: @background-color-light;
// Divider
// ---
@divider-color: rgba(255, 255, 255, 12%);
// Modal
// ---
@modal-header-bg: @popover-background;
@modal-header-border-color-split: @border-color-split;
@modal-content-bg: @popover-background;
@modal-footer-border-color-split: @border-color-split;
// Radio
// ---
@radio-solid-checked-color: @white;
@radio-dot-disabled-color: fade(@white, 20%);
// Radio buttons
// ---
@radio-disabled-button-checked-bg: fade(@white, 20%);
@radio-disabled-button-checked-color: @disabled-color;
// Layout
// ---
@layout-body-background: @body-background;
@layout-header-background: @popover-background;
@layout-trigger-background: #262626;
// Input
// ---
@input-bg: transparent;
@input-placeholder-color: fade(@white, 30%);
@input-icon-color: fade(@white, 30%);
@input-number-handler-active-bg: @item-hover-bg;
@input-icon-hover-color: fade(@white, 85%);
// Select
// ---
@select-background: transparent;
@select-dropdown-bg: @popover-background;
@select-clear-background: @component-background;
@select-selection-item-bg: fade(@white, 8);
@select-selection-item-border-color: @border-color-split;
@select-multiple-disabled-background: @component-background;
@select-multiple-item-disabled-color: #595959;
@select-multiple-item-disabled-border-color: @popover-background;
// Cascader
// ---
@cascader-bg: transparent;
@cascader-menu-bg: @popover-background;
@cascader-menu-border-color-split: @border-color-split;
// Tooltip
// ---
// Tooltip background color
@tooltip-bg: #434343;
// Menu
// ---
// dark theme
@menu-dark-inline-submenu-bg: @component-background;
@menu-dark-bg: @popover-background;
@menu-popup-bg: @popover-background;
// Message
// ---
@message-notice-content-bg: @popover-background;
// Notification
@notification-bg: @popover-background;
// LINK
@link-hover-color: @primary-5;
@link-active-color: @primary-7;
// Table
// --
@table-header-bg: #1d1d1d;
@table-body-sort-bg: fade(@white, 1%);
@table-row-hover-bg: #262626;
@table-header-cell-split-color: fade(@white, 8%);
@table-header-sort-bg: #262626;
@table-header-filter-active-bg: #434343;
@table-header-sort-active-bg: #303030;
@table-fixed-header-sort-active-bg: #222;
@table-filter-btns-bg: @popover-background;
@table-expanded-row-bg: @table-header-bg;
@table-filter-dropdown-bg: @popover-background;
@table-expand-icon-bg: transparent;
// Tag
// ---
@info-color-deprecated-bg: @primary-1;
@info-color-deprecated-border: @primary-3;
@success-color-deprecated-bg: @green-1;
@success-color-deprecated-border: @green-3;
@warning-color-deprecated-bg: @orange-1;
@warning-color-deprecated-border: @orange-3;
@error-color-deprecated-bg: @red-1;
@error-color-deprecated-border: @red-3;
// TimePicker
// ---
@picker-basic-cell-hover-with-range-color: darken(@primary-color, 35%);
@picker-basic-cell-disabled-bg: #303030;
@picker-border-color: @border-color-split;
@picker-bg: transparent;
@picker-date-hover-range-border-color: darken(@primary-color, 20%);
// Dropdown
// ---
@dropdown-menu-bg: @popover-background;
@dropdown-menu-submenu-disabled-bg: transparent;
// Steps
// ---
@steps-nav-arrow-color: fade(@white, 20%);
@steps-background: transparent;
// Avatar
// ---
@avatar-bg: fade(@white, 30%);
// Progress
// ---
@progress-steps-item-bg: fade(@white, 8%);
// Calendar
// ---
@calendar-bg: @popover-background;
@calendar-input-bg: @calendar-bg;
@calendar-border-color: transparent;
@calendar-full-bg: @component-background;
// Badge
// ---
@badge-text-color: @white;
// Popover
@popover-bg: @popover-background;
// Drawer
@drawer-bg: @popover-background;
// Card
// ---
@card-actions-background: @component-background;
@card-skeleton-bg: #303030;
@card-shadow: 0 1px 2px -2px rgba(0, 0, 0, 0.64), 0 3px 6px 0 rgba(0, 0, 0, 0.48),
0 5px 12px 4px rgba(0, 0, 0, 0.36);
// Transfer
// ---
@transfer-item-hover-bg: #262626;
// Comment
// ---
@comment-bg: transparent;
@comment-author-time-color: fade(@white, 30%);
@comment-action-hover-color: fade(@white, 65%);
// Rate
// ---
@rate-star-bg: fade(@white, 12%);
// Switch
// ---
@switch-bg: @white;
// Pagination
// ---
@pagination-item-bg: transparent;
@pagination-item-bg-active: transparent;
@pagination-item-link-bg: transparent;
@pagination-item-disabled-bg-active: fade(@white, 25%);
@pagination-item-disabled-color-active: @black;
@pagination-item-input-bg: @pagination-item-bg;
// PageHeader
// ---
@page-header-back-color: @icon-color;
@page-header-ghost-bg: transparent;
// Slider
// ---
@slider-rail-background-color: #262626;
@slider-rail-background-color-hover: @border-color-base;
@slider-dot-border-color: @border-color-split;
@slider-dot-border-color-active: @primary-4;
// Skeleton
// ---
@skeleton-to-color: fade(@white, 16%);
// Alert
// ---
@alert-success-border-color: @green-3;
@alert-success-bg-color: @green-1;
@alert-success-icon-color: @success-color;
@alert-info-border-color: @primary-3;
@alert-info-bg-color: @primary-1;
@alert-info-icon-color: @info-color;
@alert-warning-border-color: @gold-3;
@alert-warning-bg-color: @gold-1;
@alert-warning-icon-color: @warning-color;
@alert-error-border-color: @red-3;
@alert-error-bg-color: @red-1;
@alert-error-icon-color: @error-color;
// Timeline
// ---
@timeline-color: @border-color-split;
@timeline-dot-color: @primary-color;
// Mentions
// ---
@mentions-dropdown-bg: @popover-background;
// Segmented
// ---
@segmented-bg: fade(@black, 25%);
@segmented-hover-bg: fade(@black, 45%);
@segmented-selected-bg: #333333;
@segmented-label-color: fade(@white, 65%);
@segmented-label-hover-color: fade(@white, 85%);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,7 @@
// Default using variable as entry to support site variable version
// This will be replaced in webpack bundle
// @root-entry-name: variable;
// @import './default.less';
// @import './variable.less';
@import './@{root-entry-name}.less';

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,4 @@
@root-entry-name: variable;
@import './themes/variable.less';
@import './core/index';

View File

@@ -0,0 +1,165 @@
import { Space, Popconfirm, Dropdown, DatePicker, Popover, Button, Radio } from 'antd';
import { FC, useState, useRef } from 'react';
import {
PlaySquareOutlined,
StopOutlined,
CloudDownloadOutlined,
DeleteOutlined,
} from '@ant-design/icons';
export type BatchCtrlDropDownButtonProps = {
onMenuClick?: (key: string) => void;
onDownloadDateRangeChange?: (dateRange: string[], pickerType: string) => void;
onDeleteConfirm?: () => void;
downloadLoading?: boolean;
disabledList?: string[];
hiddenList?: string[];
};
const { RangePicker } = DatePicker;
const BatchCtrlDropDownButton: FC<BatchCtrlDropDownButtonProps> = ({
onMenuClick,
onDownloadDateRangeChange,
onDeleteConfirm,
downloadLoading,
disabledList = [],
hiddenList = [],
}) => {
const [popoverOpenState, setPopoverOpenState] = useState<boolean>(false);
const [pickerType, setPickerType] = useState<string>('day');
const dateRangeRef = useRef<any>([]);
const dropdownButtonItems: any[] = [
{
key: 'batchStart',
label: '批量启用',
icon: <PlaySquareOutlined />,
disabled: disabledList?.includes('batchStart'),
},
{
key: 'batchStop',
label: '批量停用',
icon: <StopOutlined />,
disabled: disabledList?.includes('batchStop'),
},
{
key: 'batchDownload',
label: (
<a
onClick={() => {
setPopoverOpenState(true);
}}
>
</a>
),
icon: <CloudDownloadOutlined />,
disabled: disabledList?.includes('batchDownload'),
},
{
key: 'batchDeleteDivider',
type: 'divider',
},
{
key: 'batchDelete',
label: (
<Popconfirm
title="确定批量删除吗?"
onConfirm={() => {
onDeleteConfirm?.();
}}
>
<a></a>
</Popconfirm>
),
icon: <DeleteOutlined />,
disabled: disabledList?.includes('batchDelete'),
},
].filter((item) => {
return !hiddenList.includes(item.key);
});
const popoverConfig = {
title: '选择下载区间',
content: (
<Space direction="vertical">
<Radio.Group
size="small"
value={pickerType}
onChange={(e) => {
setPickerType(e.target.value);
}}
>
<Radio.Button value="day"></Radio.Button>
<Radio.Button value="week"></Radio.Button>
<Radio.Button value="month"></Radio.Button>
</Radio.Group>
<RangePicker
style={{ paddingBottom: 5 }}
onChange={(date) => {
dateRangeRef.current = date;
return;
}}
picker={pickerType as any}
allowClear={true}
/>
<div style={{ marginTop: 20 }}>
<Space>
<Button
type="primary"
loading={downloadLoading}
onClick={() => {
const [startMoment, endMoment] = dateRangeRef.current;
let searchDateRange = [
startMoment?.format('YYYY-MM-DD'),
endMoment?.format('YYYY-MM-DD'),
];
if (pickerType === 'week') {
searchDateRange = [
startMoment?.startOf('isoWeek').format('YYYY-MM-DD'),
endMoment?.startOf('isoWeek').format('YYYY-MM-DD'),
];
}
if (pickerType === 'month') {
searchDateRange = [
startMoment?.startOf('month').format('YYYY-MM-DD'),
endMoment?.startOf('month').format('YYYY-MM-DD'),
];
}
onDownloadDateRangeChange?.(searchDateRange, pickerType);
}}
>
</Button>
</Space>
</div>
</Space>
),
};
return (
<Popover
content={popoverConfig?.content}
title={popoverConfig?.title}
trigger="click"
key="ctrlBtnList"
open={popoverOpenState}
placement="bottomLeft"
onOpenChange={(open: boolean) => {
setPopoverOpenState(open);
}}
>
<Dropdown.Button
menu={{
items: dropdownButtonItems,
onClick: ({ key }: { key: string }) => {
onMenuClick?.(key);
},
}}
>
</Dropdown.Button>
</Popover>
);
};
export default BatchCtrlDropDownButton;

View File

@@ -1,12 +1,13 @@
@import '~antd/es/style/themes/default.less';
.container > * {
background-color: @popover-bg;
background-color: #fff;
border-radius: 4px;
box-shadow: @shadow-1-down;
box-shadow: 0 6px 16px -8px rgba(0, 0, 0, 0.08), 0 9px 28px 0 rgba(0, 0, 0, 0.05),
0 12px 48px 16px rgba(0, 0, 0, 0.03);
}
@media screen and (max-width: @screen-xs) {
@media screen and (max-width: 480px) {
.container {
width: 100% !important;
}

View File

@@ -0,0 +1,30 @@
import React, { useState, useEffect } from 'react';
import { StarOutlined, StarFilled } from '@ant-design/icons';
import styles from './style.less';
type Props = {
star?: boolean;
onToggleCollect: (star: boolean) => void;
};
const MStar: React.FC<Props> = ({ star = false, onToggleCollect }) => {
const [starState, setStarState] = useState(star);
useEffect(() => {
setStarState(star);
}, [star]);
return (
<div
className={`${styles.collectDashboard} ${starState === true ? 'dashboardCollected' : ''}`}
onClick={(event) => {
event.stopPropagation();
setStarState(!starState);
onToggleCollect(!starState);
}}
>
{starState === false ? <StarOutlined /> : <StarFilled style={{ color: '#eac54f' }} />}
</div>
);
};
export default MStar;

View File

@@ -0,0 +1,6 @@
.collectDashboard {
color: #546174;
&.dashboardCollected {
display: block !important;
}
}

View File

@@ -54,7 +54,7 @@ const AvatarDropdown: React.FC<GlobalHeaderRightProps> = () => {
<HeaderDropdown menu={{ items }} disabled={APP_TARGET === 'inner'}>
<span className={`${styles.action} ${styles.account}`}>
<TMEAvatar className={styles.avatar} size="small" staffName={currentUser.staffName} />
<span className={cx(styles.name, 'anticon')}>{currentUser.staffName}</span>
<span style={{ color: '#fff' }}>{currentUser.staffName}</span>
</span>
</HeaderDropdown>
);

View File

@@ -1,4 +1,4 @@
@import '~antd/es/style/themes/default.less';
@pro-header-hover-bg: rgba(0, 0, 0, 0.025);
@@ -26,15 +26,17 @@
padding: 0 12px;
cursor: pointer;
transition: all 0.3s;
color: #fff;
>span {
vertical-align: middle;
}
&:hover {
background: @pro-header-hover-bg;
background: #296df3;
}
&:global(.opened) {
background: @pro-header-hover-bg;
}
@@ -57,49 +59,3 @@
}
}
}
.dark {
.action {
.download {
display: flex;
align-items: center;
}
.menuName {
margin-left: 5px;
color: #fff;
font-size: 13px;
}
&:hover {
background: #296df3;
}
&:global(.opened) {
background: #252a3d;
}
}
}
.actionIcon {
font-size: 20px;
}
.tooltip {
padding-top: 0 !important;
font-size: 12px !important;
:global {
.ant-tooltip-arrow {
display: none;
}
.ant-tooltip-inner {
min-height: 0 !important;
padding: 3px 6px !important;
}
}
}

View File

@@ -5,7 +5,6 @@ import Avatar from './AvatarDropdown';
import { SettingOutlined } from '@ant-design/icons';
import { getSystemConfig } from '@/services/user';
import styles from './index.less';
import cx from 'classnames';
export type SiderTheme = 'light' | 'dark';
@@ -21,27 +20,20 @@ const GlobalHeaderRight: React.FC = () => {
}
const { currentUser = {} } = initialState as any;
const { layout } = initialState.settings;
let className = styles.right;
const querySystemConfig = async () => {
const { code, data } = await getSystemConfig();
if (code === 200) {
const { admins } = data;
if (admins.includes(currentUser?.staffName)) {
if (Array.isArray(admins) && admins.includes(currentUser?.staffName)) {
setHasSettingPermisson(true);
}
}
};
if (layout === 'top' || layout === 'mix') {
className = cx(styles.right, styles.dark);
}
function handleLogin() {}
return (
<Space className={className} style={{ marginRight: -8 }}>
<Space className={styles.right}>
<Avatar onClickLogin={handleLogin} />
{hasSettingPermisson && (
<span

View File

@@ -1,4 +1,3 @@
@import '~antd/es/style/themes/default.less';
.standardFormRow {
display: flex;
@@ -15,7 +14,7 @@
.ant-legacy-form-item-label {
label {
margin-right: 0;
color: @text-color;
color: fade(#000, 85%);
}
}
.ant-form-item-label,
@@ -29,8 +28,8 @@
.label {
flex: 0 0 auto;
margin-right: 12px;
color: @heading-color;
font-size: @font-size-base;
color: fade(#000, 85%);
font-size: 14px;
text-align: left;
& > span {
display: inline-block;

View File

@@ -1,4 +1,3 @@
@import '~antd/es/style/themes/default.less';
.tagSelect {
position: relative;
@@ -12,7 +11,7 @@
.ant-tag {
margin-right: 24px;
padding: 0 8px;
font-size: @font-size-base;
font-size: 14px;
}
}
&.expanded {

View File

@@ -1,4 +1,4 @@
@import '~antd/es/style/themes/default.less';
:root:root {
--primary-color: #f87653;
@@ -30,6 +30,7 @@ body,
height: 100%;
font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Microsoft YaHei,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";
-webkit-font-smoothing:antialiased;
margin: 0;
}
.colorWeak {
@@ -55,7 +56,7 @@ ol {
list-style: none;
}
@media (max-width: @screen-xs) {
@media (max-width: 480px) {
.ant-table {
width: 100%;
overflow-x: auto;
@@ -111,26 +112,46 @@ ol {
.ant-spin-spinning {
max-height: none !important;
}
.loadingPlaceholder {
height: 100vh;
}
}
.ant-pro-layout .ant-pro-layout-content {
padding: 0;
background-color: #f0f2f5;
}
.ellipsis {
overflow: hidden;
text-overflow: ellipsis;
}
.ant-menu-dark.ant-menu-horizontal>.ant-menu-item,
.ant-menu-dark.ant-menu-horizontal>.ant-menu-submenu {
.ant-pro-top-nav-header-menu {
padding: 0!important;
line-height: unset!important;
}
.ant-menu-root>.ant-menu-item,
.ant-menu-root>.ant-menu-submenu {
font-weight: bold;
font-size: 14px;
&>span>a,
&>a {
color: white;
&:hover {
color: white;
}
}
&:hover {
background-color: #296DF3!important;
}
}
.ant-menu-root>.ant-menu-item.ant-menu-item-selected {
background-color: #296DF3!important;
color: white;
}
.ant-pro-top-nav-header-logo h1 {
@@ -143,63 +164,19 @@ ol {
backdrop-filter: blur(10px) !important;
}
.ant-menu.ant-menu-dark {
color: #fff;
}
.ant-menu-submenu-selected {
background-color: #296DF3 !important;
}
.ant-menu.ant-menu-dark .ant-menu-sub {
background-color: #fff;
.ant-menu-item-selected {
background-color: #296DF3;
.ant-pro-menu-item-title {
color: #fff;
}
}
.ant-menu-item {
&:hover {
background-color: #e3e3e3;
.ant-menu-item-selected {
background-color: #e3e3e3;
.ant-pro-menu-item-title {
color: #181a1a !important;
}
}
.ant-pro-menu-item-title {
color: #181a1a !important;
}
}
& > span > a {
color: #181a1a;
&:hover {
color: #181a1a;
}
}
}
}
.ant-menu-item:active {
background: inherit
}
// .ant-menu-dark .ant-menu-item > span > a {
// color: #181a1a;
// &:hover {
// color: #fff;
// }
// }
// .ant-menu-dark.ant-menu-dark:not(.ant-menu-horizontal) .ant-menu-item-selected {
// // color: #fff;
// background-color: #1b4aef;
// .ant-menu-submenu-selected {
// background-color: #296DF3 !important;
// }
.customizeHeader {
background-color: rgba(0, 0, 0, 0.2);
backdrop-filter: blur(10px);
}
// .ant-menu-item:active {
// background: inherit
// }
.ant-pro-top-nav-header-main-left {
min-width: 100px !important;
@@ -210,64 +187,31 @@ ol {
}
.link {
color: #296df3;
cursor: pointer;
}
// .link {
// color: #296df3;
// cursor: pointer;
// }
.closeTab {
position: relative;
width: 10px;
height: 10px;
// opacity: 0;
}
.closeTab::before,
.closeTab::after {
position: absolute;
top: -2px;
left: 0;
width: 1px;
height: 10px;
background-color: rgb(50, 50, 50);
content: ' ';
}
.closeTab::before {
transform: rotate(45deg);
}
.closeTab::after {
transform: rotate(-45deg);
}
.dot {
float: right;
width: 8px;
height: 8px;
background: #bfbfbf;
border-radius: 100%;
}
.bdWrapper {
margin: -24px;
.ant-layout-sider {
top: 48px !important;
}
}
// .dot {
// float: right;
// width: 8px;
// height: 8px;
// background: #bfbfbf;
// border-radius: 100%;
// }
.logo {
position: relative;
padding-bottom: 5px;
color: #fff;
font-size: 20px;
font-weight: 700;
padding-right: 50px;
}
.ant-notification-topRight {
right: 240px !important;
}
// .ant-notification-topRight {
// right: 240px !important;
// }
.g6ContextMenuContainer {

View File

@@ -1,7 +1,8 @@
import React, { useEffect, useState } from 'react';
import React, { useState } from 'react';
import { Form, Input, Spin, Select, message } from 'antd';
import type { FormInstance } from 'antd/lib/form';
import { getDbNames, getTables } from '../../service';
import SqlEditor from '@/components/SqlEditor';
import { ISemantic } from '../../data';
const FormItem = Form.Item;
@@ -123,6 +124,13 @@ const DataSourceBasicForm: React.FC<Props> = ({ isEdit, databaseConfigList, mode
<FormItem name="description" label="数据源描述">
<TextArea placeholder="请输入数据源描述" />
</FormItem>
{/* <FormItem
name="filterSql"
label="过滤SQL"
tooltip="主要用于词典导入场景, 对维度值进行过滤 格式: field1 = 'xxx' and field2 = 'yyy'"
>
<SqlEditor height={'150px'} />
</FormItem> */}
</Spin>
);
};

View File

@@ -24,7 +24,7 @@ export type CreateFormProps = {
basicInfoFormMode?: 'normal' | 'fast';
onDataBaseTableChange?: (tableName: string) => void;
onDataSourceBtnClick?: () => void;
// modalSolt: ReactNode;
onOpenDataSourceEdit?: () => void;
};
const { Step } = Steps;
@@ -45,6 +45,7 @@ const DataSourceCreateForm: React.FC<CreateFormProps> = ({
databaseId,
basicInfoFormMode,
onDataSourceBtnClick,
onOpenDataSourceEdit,
children,
}) => {
const isEdit = !!dataSourceItem?.id;
@@ -59,7 +60,7 @@ const DataSourceCreateForm: React.FC<CreateFormProps> = ({
const updateFormVal = (val: any) => {
formValRef.current = val;
};
const [sqlFilter, setSqlFilter] = useState<string>('');
useEffect(() => {
const hasEmpty = fields.some((item) => {
const { name, isCreateDimension, isCreateMetric } = item;
@@ -170,6 +171,7 @@ const DataSourceCreateForm: React.FC<CreateFormProps> = ({
queryType: basicInfoFormMode === 'fast' ? 'table_query' : 'sql_query',
tableQuery: dbName && tableName ? `${dbName}.${tableName}` : '',
modelId: isEdit ? dataSourceItem.modelId : modelId,
filterSql: sqlFilter,
};
const queryDatasource = isEdit ? updateDatasource : createDatasource;
const { code, msg, data } = await queryDatasource(queryParams);
@@ -221,7 +223,7 @@ const DataSourceCreateForm: React.FC<CreateFormProps> = ({
const initData = async () => {
const { queryType, tableQuery } = dataSourceItem.datasourceDetail;
let tableQueryInitValue = {};
let columns = fieldColumns;
let columns = fieldColumns || [];
if (queryType === 'table_query') {
const tableQueryString = tableQuery || '';
const [dbName, tableName] = tableQueryString.split('.');
@@ -235,7 +237,8 @@ const DataSourceCreateForm: React.FC<CreateFormProps> = ({
};
const formatterInitData = (columns: any[], extendParams: Record<string, any> = {}) => {
const { id, name, bizName, description, datasourceDetail, databaseId } = dataSourceItem as any;
const { id, name, bizName, description, datasourceDetail, databaseId, filterSql } =
dataSourceItem as any;
const { dimensions, identifiers, measures } = datasourceDetail;
const initValue = {
id,
@@ -243,6 +246,7 @@ const DataSourceCreateForm: React.FC<CreateFormProps> = ({
bizName,
description,
databaseId,
filterSql,
...extendParams,
// ...tableQueryInitValue,
};
@@ -250,6 +254,7 @@ const DataSourceCreateForm: React.FC<CreateFormProps> = ({
...formValRef.current,
...initValue,
};
setSqlFilter(filterSql);
updateFormVal(editInitFormVal);
form.setFieldsValue(initValue);
const formatFields = [
@@ -261,10 +266,24 @@ const DataSourceCreateForm: React.FC<CreateFormProps> = ({
};
useEffect(() => {
if (isEdit) {
initData();
} else {
initFields([], fieldColumns);
const { queryType } = dataSourceItem?.datasourceDetail || {};
if (queryType === 'table_query') {
if (isEdit) {
initData();
} else {
initFields([], fieldColumns);
}
}
}, [dataSourceItem]);
useEffect(() => {
const { queryType } = dataSourceItem?.datasourceDetail || {};
if (queryType !== 'table_query') {
if (isEdit) {
initData();
} else {
initFields([], fieldColumns);
}
}
}, [dataSourceItem, fieldColumns]);
@@ -306,7 +325,14 @@ const DataSourceCreateForm: React.FC<CreateFormProps> = ({
return (
<>
<div style={{ display: currentStep === 1 ? 'block' : 'none' }}>
<FieldForm fields={fields} onFieldChange={handleFieldChange} />
<FieldForm
fields={fields}
onFieldChange={handleFieldChange}
onSqlChange={(sql) => {
setSqlFilter(sql);
}}
sql={sqlFilter}
/>
</div>
<div style={{ display: currentStep !== 1 ? 'block' : 'none' }}>
<DataSourceBasicForm
@@ -357,6 +383,9 @@ const DataSourceCreateForm: React.FC<CreateFormProps> = ({
type="primary"
onClick={() => {
handleNext();
if (!isEdit && Array.isArray(fields) && fields.length === 0) {
onOpenDataSourceEdit?.();
}
}}
>
@@ -381,7 +410,7 @@ const DataSourceCreateForm: React.FC<CreateFormProps> = ({
<Modal
forceRender
width={1300}
bodyStyle={{ padding: '32px 40px 48px' }}
styles={{ padding: '32px 40px 48px' }}
destroyOnClose
title={`${isEdit ? '编辑' : '新建'}数据源`}
maskClosable={false}

View File

@@ -1,9 +1,9 @@
import React from 'react';
import { Table, Select, Checkbox, Input, Alert, Space, Tooltip } from 'antd';
import { Table, Select, Checkbox, Input, Alert, Space, Tooltip, Form } from 'antd';
import TableTitleTooltips from '../../components/TableTitleTooltips';
import { isUndefined } from 'lodash';
import { ExclamationCircleOutlined } from '@ant-design/icons';
import Marquee from 'react-fast-marquee';
import SqlEditor from '@/components/SqlEditor';
import {
TYPE_OPTIONS,
DATE_FORMATTER,
@@ -24,7 +24,11 @@ type FieldItem = {
timeGranularity?: string;
};
const FormItem = Form.Item;
type Props = {
onSqlChange: (sql: string) => void;
sql: string;
fields: FieldItem[];
onFieldChange: (fieldName: string, data: Partial<FieldItem>) => void;
};
@@ -41,7 +45,7 @@ const getCreateFieldName = (type: EnumDataSourceType) => {
// const editState = !isUndefined(record[isCreateName]) ? !!record[isCreateName] : true;
};
const FieldForm: React.FC<Props> = ({ fields, onFieldChange }) => {
const FieldForm: React.FC<Props> = ({ fields, sql, onFieldChange, onSqlChange }) => {
const handleFieldChange = (record: FieldItem, fieldName: string, value: any) => {
onFieldChange(record.bizName, {
...record,
@@ -271,6 +275,14 @@ const FieldForm: React.FC<Props> = ({ fields, onFieldChange }) => {
pagination={false}
scroll={{ y: 500 }}
/>
<FormItem
style={{ marginTop: 40, marginBottom: 60 }}
name="filterSql"
label={<span style={{ fontSize: 14 }}>SQL</span>}
tooltip="主要用于词典导入场景, 对维度值进行过滤 格式: field1 = 'xxx' and field2 = 'yyy'"
>
<SqlEditor height={'150px'} value={sql} onChange={onSqlChange} />
</FormItem>
</>
);
};

View File

@@ -15,6 +15,7 @@ import MetricTrendSection from '@/pages/SemanticModel/Metric/components/MetricTr
import { SENSITIVE_LEVEL_ENUM, SENSITIVE_LEVEL_COLOR } from '../constant';
import type { TabsProps } from 'antd';
import { ISemantic } from '../data';
import MetricStar from './components/MetricStar';
const { Content } = Layout;
type Props = {
@@ -31,20 +32,6 @@ interface DescriptionItemProps {
icon: ReactNode;
}
// type InfoListItemChildrenItem = {
// label: string;
// value: string;
// content?: ReactNode;
// hideItem?: boolean;
// };
// type InfoListItem = {
// title: string;
// hideItem?: boolean;
// render?: () => ReactNode;
// children?: InfoListItemChildrenItem[];
// };
const DescriptionItem = ({ title, content, icon }: DescriptionItemProps) => (
<Tooltip title={title}>
<div style={{ width: 'max-content', fontSize: 14, color: '#546174' }}>
@@ -59,10 +46,7 @@ const DescriptionItem = ({ title, content, icon }: DescriptionItemProps) => (
const MetricDetail: React.FC<Props> = ({ domainManger }) => {
const params: any = useParams();
const metricId = params.metricId;
// const bizName = params.bizName;
// const [infoList, setInfoList] = useState<InfoListItem[]>([]);
// const { selectModelName } = domainManger;
const [metircData, setMetircData] = useState<ISemantic.IMetricItem>();
useEffect(() => {
queryMetricData(metricId);
@@ -77,112 +61,20 @@ const MetricDetail: React.FC<Props> = ({ domainManger }) => {
message.error(msg);
};
// useEffect(() => {
// if (!metircData) {
// return;
// }
// const {
// alias,
// bizName,
// createdBy,
// createdAt,
// updatedAt,
// description,
// sensitiveLevel,
// modelName,
// } = metircData;
// const list = [
// {
// title: '基本信息',
// children: [
// {
// label: '字段名称',
// value: bizName,
// },
// {
// label: '别名',
// hideItem: !alias,
// value: alias || '-',
// },
// {
// label: '所属模型',
// value: modelName,
// content: <Tag>{modelName || selectModelName}</Tag>,
// },
// {
// label: '描述',
// value: description,
// },
// ],
// },
// {
// title: '应用信息',
// children: [
// {
// label: '敏感度',
// value: SENSITIVE_LEVEL_ENUM[sensitiveLevel],
// },
// ],
// },
// // {
// // title: '指标趋势',
// // render: () => (
// // <Row key={`metricTrendSection`} style={{ marginBottom: 10, display: 'flex' }}>
// // <Col span={24}>
// // <MetricTrendSection nodeData={metircData} />
// // </Col>
// // </Row>
// // ),
// // },
// {
// title: '创建信息',
// children: [
// {
// label: '创建人',
// value: createdBy,
// },
// {
// label: '创建时间',
// value: createdAt ? moment(createdAt).format('YYYY-MM-DD HH:mm:ss') : '',
// },
// {
// label: '更新时间',
// value: updatedAt ? moment(updatedAt).format('YYYY-MM-DD HH:mm:ss') : '',
// },
// ],
// },
// ];
// setInfoList(list);
// }, [metircData]);
const tabItems: TabsProps['items'] = [
{
key: 'metricTrend',
label: '图表',
children: <MetricTrendSection metircData={metircData} />,
},
// {
// key: '2',
// label: 'Tab 2',
// children: 'Content of Tab Pane 2',
// },
];
const contentStyle: React.CSSProperties = {
minHeight: 120,
color: '#fff',
// marginRight: 15,
backgroundColor: '#fff',
};
// const siderStyle: React.CSSProperties = {
// width: '300px',
// backgroundColor: '#fff',
// };
return (
<Layout className={styles.metricDetail}>
<Layout>
@@ -200,20 +92,21 @@ const MetricDetail: React.FC<Props> = ({ domainManger }) => {
<div className={styles.navContainer}>
<Space>
<MetricStar metricId={metricId} initState={metircData?.isCollect} />
<span style={{ color: '#296DF3' }}>
{metircData?.name}
{metircData?.alias && `[${metircData.alias}]`}
</span>
{metircData?.name && (
<>
<span style={{ position: 'relative', top: '-2px' }}> | </span>
<span style={{ position: 'relative', top: '-2px', color: '#c3c3c3' }}>|</span>
<span style={{ fontSize: 16, color: '#296DF3' }}>{metircData?.bizName}</span>
</>
)}
{metircData?.sensitiveLevel !== undefined && (
<span style={{ position: 'relative', top: '-2px' }}>
<Tag color={SENSITIVE_LEVEL_COLOR[metircData.sensitiveLevel]}>
{SENSITIVE_LEVEL_ENUM[metircData.sensitiveLevel]}
{SENSITIVE_LEVEL_ENUM[metircData.sensitiveLevel]}
</Tag>
</span>
)}
@@ -235,19 +128,6 @@ const MetricDetail: React.FC<Props> = ({ domainManger }) => {
<div className={styles.titleRight}>
{metircData?.createdBy ? (
<>
{/* <div className={styles.info}>
<DescriptionItem
title="所属模型"
icon={<UserOutlined />}
content={metircData?.modelName}
/>
<DescriptionItem
title="更新时间"
icon={<CalendarOutlined />}
content={metircData?.description}
/>
</div> */}
<div className={styles.info}>
<DescriptionItem
title="创建人"
@@ -272,41 +152,6 @@ const MetricDetail: React.FC<Props> = ({ domainManger }) => {
</div>
</Content>
</Layout>
{/* <Sider style={siderStyle} width={400}>
<>
<div key={metircData?.id} className={styles.metricInfoContent}>
{infoList.map((item) => {
const { children, title, render } = item;
return (
<div key={title} style={{ display: item.hideItem ? 'none' : 'block' }}>
<p className={styles.title}>{title}</p>
{render?.() ||
(Array.isArray(children) &&
children.map((childrenItem) => {
return (
<Row
key={`${childrenItem.label}-${childrenItem.value}`}
style={{
marginBottom: 10,
display: childrenItem.hideItem ? 'none' : 'flex',
}}
>
<Col span={24}>
<DescriptionItem
title={childrenItem.label}
content={childrenItem.content || childrenItem.value}
/>
</Col>
</Row>
);
}))}
<Divider />
</div>
);
})}
</div>
</>
</Sider> */}
</Layout>
);
};

View File

@@ -1,17 +1,6 @@
import type { ActionType, ProColumns } from '@ant-design/pro-table';
import ProTable from '@ant-design/pro-table';
import {
message,
Space,
Popconfirm,
Tag,
Spin,
Dropdown,
DatePicker,
Popover,
Button,
Radio,
} from 'antd';
import { message, Space, Popconfirm, Tag, Spin, Tooltip } from 'antd';
import React, { useRef, useState, useEffect } from 'react';
import type { Dispatch } from 'umi';
import { connect, history, useModel } from 'umi';
@@ -28,23 +17,17 @@ import MetricInfoCreateForm from '../components/MetricInfoCreateForm';
import MetricCardList from './components/MetricCardList';
import NodeInfoDrawer from '../SemanticGraph/components/NodeInfoDrawer';
import { SemanticNodeType, StatusEnum } from '../enum';
import moment, { Moment } from 'moment';
import moment from 'moment';
import styles from './style.less';
import { ISemantic } from '../data';
import {
PlaySquareOutlined,
StopOutlined,
CloudDownloadOutlined,
DeleteOutlined,
} from '@ant-design/icons';
import BatchCtrlDropDownButton from '@/components/BatchCtrlDropDownButton';
import MetricStar from './components/MetricStar';
type Props = {
dispatch: Dispatch;
domainManger: StateType;
};
const { RangePicker } = DatePicker;
type QueryMetricListParams = {
id?: string;
name?: string;
@@ -58,7 +41,6 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
const { initialState = {} } = useModel('@@initialState');
const { currentUser = {} } = initialState as any;
const { selectDomainId, selectModelId: modelId } = domainManger;
const [createModalVisible, setCreateModalVisible] = useState<boolean>(false);
const defaultPagination = {
@@ -75,10 +57,7 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
showType: localStorage.getItem('metricMarketShowType') === '1' ? true : false,
});
const [infoDrawerVisible, setInfoDrawerVisible] = useState<boolean>(false);
const [popoverOpenState, setPopoverOpenState] = useState<boolean>(false);
const [pickerType, setPickerType] = useState<string>('day');
const dateRangeRef = useRef<any>([]);
const [downloadLoading, setDownloadLoading] = useState<boolean>(false);
const actionRef = useRef<ActionType>();
@@ -152,7 +131,11 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
}
};
const downloadMetricQuery = async (ids: React.Key[], dateStringList: string[]) => {
const downloadMetricQuery = async (
ids: React.Key[],
dateStringList: string[],
pickerType: string,
) => {
if (Array.isArray(ids) && ids.length > 0) {
setDownloadLoading(true);
const [startDate, endDate] = dateStringList;
@@ -166,7 +149,6 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
},
});
setDownloadLoading(false);
setPopoverOpenState(false);
}
};
@@ -184,14 +166,18 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
dataIndex: 'name',
title: '指标名称',
render: (_, record: any) => {
const { id, isCollect } = record;
return (
<a
onClick={() => {
history.push(`/metric/detail/${record.id}`);
}}
>
{record.name}
</a>
<Space>
<MetricStar metricId={id} initState={isCollect} />
<a
onClick={() => {
history.push(`/metric/detail/${record.id}`);
}}
>
{record.name}
</a>
</Space>
);
},
},
@@ -343,43 +329,7 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
}),
};
const dropdownButtonItems: any[] = [
{
key: 'batchStart',
label: '批量启用',
icon: <PlaySquareOutlined />,
},
{
key: 'batchStop',
label: '批量停用',
icon: <StopOutlined />,
},
{
key: 'batchDownload',
// label: '批量下载',
label: <a></a>,
icon: <CloudDownloadOutlined />,
},
{
type: 'divider',
},
{
key: 'batchDelete',
label: (
<Popconfirm
title="确定批量删除吗?"
onConfirm={() => {
queryBatchUpdateStatus(selectedRowKeys, StatusEnum.DELETED);
}}
>
<a></a>
</Popconfirm>
),
icon: <DeleteOutlined />,
},
];
const onMenuClick = ({ key }: { key: string }) => {
const onMenuClick = (key: string) => {
switch (key) {
case 'batchStart':
queryBatchUpdateStatus(selectedRowKeys, StatusEnum.ONLINE);
@@ -387,69 +337,11 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
case 'batchStop':
queryBatchUpdateStatus(selectedRowKeys, StatusEnum.OFFLINE);
break;
case 'batchDownload':
setPopoverOpenState(true);
default:
break;
}
};
const popoverConfig = {
title: '选择下载区间',
content: (
<Space direction="vertical">
<Radio.Group
size="small"
value={pickerType}
onChange={(e) => {
setPickerType(e.target.value);
}}
>
<Radio.Button value="day"></Radio.Button>
<Radio.Button value="week"></Radio.Button>
<Radio.Button value="month"></Radio.Button>
</Radio.Group>
<RangePicker
style={{ paddingBottom: 5 }}
onChange={(date) => {
dateRangeRef.current = date;
return;
}}
picker={pickerType as any}
allowClear={true}
/>
<div style={{ marginTop: 20 }}>
<Space>
<Button
type="primary"
loading={downloadLoading}
onClick={() => {
const [startMoment, endMoment] = dateRangeRef.current;
let searchDateRange = [
startMoment?.format('YYYY-MM-DD'),
endMoment?.format('YYYY-MM-DD'),
];
if (pickerType === 'week') {
searchDateRange = [
startMoment?.startOf('isoWeek').format('YYYY-MM-DD'),
endMoment?.startOf('isoWeek').format('YYYY-MM-DD'),
];
}
if (pickerType === 'month') {
searchDateRange = [
startMoment?.startOf('month').format('YYYY-MM-DD'),
endMoment?.startOf('month').format('YYYY-MM-DD'),
];
}
downloadMetricQuery(selectedRowKeys, searchDateRange);
}}
>
</Button>
</Space>
</div>
</Space>
),
};
return (
<>
<div className={styles.metricFilterWrapper}>
@@ -499,21 +391,17 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
...rowSelection,
}}
toolBarRender={() => [
<Popover
content={popoverConfig?.content}
title={popoverConfig?.title}
trigger="click"
<BatchCtrlDropDownButton
key="ctrlBtnList"
open={popoverOpenState}
placement="bottomLeft"
onOpenChange={(open: boolean) => {
setPopoverOpenState(open);
downloadLoading={downloadLoading}
onDeleteConfirm={() => {
queryBatchUpdateStatus(selectedRowKeys, StatusEnum.DELETED);
}}
>
<Dropdown.Button menu={{ items: dropdownButtonItems, onClick: onMenuClick }}>
</Dropdown.Button>
</Popover>,
onMenuClick={onMenuClick}
onDownloadDateRangeChange={(searchDateRange, pickerType) => {
downloadMetricQuery(selectedRowKeys, searchDateRange, pickerType);
}}
/>,
]}
loading={loading}
onChange={(data: any) => {

View File

@@ -0,0 +1,38 @@
import { Tooltip, message } from 'antd';
import React, { useState } from 'react';
import { metricStarState } from '../../service';
import MStar from '@/components/MStar';
type Props = {
metricId: number;
initState?: boolean;
onChange?: (state: boolean) => void;
};
const MetricStar: React.FC<Props> = ({ metricId, initState = false }) => {
const [star, setStar] = useState<boolean>(initState);
const starStateChange = async (id: number, state: boolean) => {
const { code, msg } = await metricStarState({ id, state });
if (code === 200) {
setStar(state);
} else {
message.error(msg);
}
};
return (
<Tooltip title={`${star ? '取消' : '加入'}收藏`}>
<div>
<MStar
star={star}
onToggleCollect={(star: boolean) => {
starStateChange(metricId, star);
}}
/>
</div>
</Tooltip>
);
};
export default MetricStar;

View File

@@ -120,7 +120,8 @@
height: calc(100vh - 50px);
.title {
margin-bottom: 0;
// padding: 20px;
margin-top:0;
background-color: #fff;
font-size: 20px;
line-height: 34px;
border-bottom: 1px solid #d9d9d9;

View File

@@ -203,7 +203,7 @@ const OverviewContainer: React.FC<Props> = ({ mode, domainManger, dispatch }) =>
<Helmet title={'语义模型-超音数'} />
<div className={styles.projectManger}>
<div className={styles.sider}>
<div className={styles.domainTitle}>
{/* <div className={styles.domainTitle}>
<Space>
{selectDomainName ? `${selectDomainName}` : '主题域信息'}
{selectModelName && (
@@ -213,7 +213,7 @@ const OverviewContainer: React.FC<Props> = ({ mode, domainManger, dispatch }) =>
</>
)}
</Space>
</div>
</div> */}
<DomainListTree
createDomainBtnVisible={mode === 'domain' ? true : false}
onTreeSelected={(domainData) => {

View File

@@ -118,7 +118,7 @@ const ClassDataSourceTypeModal: React.FC<Props> = ({
cover={
<CoffeeOutlined
width={240}
style={{ paddingTop: '45px', height: 120, fontSize: '48px', color: '#1890ff' }}
style={{ paddingTop: '45px', height: 75, fontSize: '48px', color: '#1890ff' }}
/>
}
>
@@ -137,7 +137,7 @@ const ClassDataSourceTypeModal: React.FC<Props> = ({
style={{ height: 220 }}
cover={
<ConsoleSqlOutlined
style={{ paddingTop: '45px', height: 120, fontSize: '48px', color: '#1890ff' }}
style={{ paddingTop: '45px', height: 75, fontSize: '48px', color: '#1890ff' }}
/>
}
>
@@ -185,6 +185,9 @@ const ClassDataSourceTypeModal: React.FC<Props> = ({
onDataSourceBtnClick={() => {
setDataSourceEditOpen(true);
}}
onOpenDataSourceEdit={() => {
setDataSourceEditOpen(true);
}}
>
<Drawer
width={'100%'}

View File

@@ -18,6 +18,7 @@ import DimensionValueSettingModal from './DimensionValueSettingModal';
import { updateDimension } from '../service';
import { ISemantic, IDataSource } from '../data';
import moment from 'moment';
import BatchCtrlDropDownButton from '@/components/BatchCtrlDropDownButton';
import styles from './style.less';
type Props = {
@@ -328,7 +329,7 @@ const ClassDimensionTable: React.FC<Props> = ({ domainManger, dispatch }) => {
},
];
const onMenuClick = ({ key }: { key: string }) => {
const onMenuClick = (key: string) => {
switch (key) {
case 'batchStart':
queryBatchUpdateStatus(selectedRowKeys, StatusEnum.ONLINE);
@@ -386,12 +387,14 @@ const ClassDimensionTable: React.FC<Props> = ({ domainManger, dispatch }) => {
>
</Button>,
<Dropdown.Button
<BatchCtrlDropDownButton
key="ctrlBtnList"
menu={{ items: dropdownButtonItems, onClick: onMenuClick }}
>
</Dropdown.Button>,
onDeleteConfirm={() => {
queryBatchUpdateStatus(selectedRowKeys, StatusEnum.DELETED);
}}
hiddenList={['batchDownload']}
onMenuClick={onMenuClick}
/>,
]}
/>

View File

@@ -1,16 +1,21 @@
import type { ActionType, ProColumns } from '@ant-design/pro-table';
import ProTable from '@ant-design/pro-table';
import { message, Button, Space, Popconfirm, Input, Tag, Dropdown } from 'antd';
import { message, Button, Space, Popconfirm, Input, Tag } from 'antd';
import React, { useRef, useState } from 'react';
import type { Dispatch } from 'umi';
import { StatusEnum } from '../enum';
import { connect } from 'umi';
import type { StateType } from '../model';
import { SENSITIVE_LEVEL_ENUM } from '../constant';
import { queryMetric, deleteMetric, updateExprMetric, batchUpdateMetricStatus } from '../service';
import {
queryMetric,
deleteMetric,
batchUpdateMetricStatus,
batchDownloadMetric,
} from '../service';
import MetricInfoCreateForm from './MetricInfoCreateForm';
import BatchCtrlDropDownButton from '@/components/BatchCtrlDropDownButton';
import moment from 'moment';
import styles from './style.less';
import { ISemantic } from '../data';
@@ -32,20 +37,7 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
});
const actionRef = useRef<ActionType>();
const updateStatus = async (data: ISemantic.IMetricItem) => {
const { code, msg } = await updateExprMetric(data);
if (code === 200) {
actionRef?.current?.reload();
dispatch({
type: 'domainManger/queryMetricList',
payload: {
modelId,
},
});
return;
}
message.error(msg);
};
const [downloadLoading, setDownloadLoading] = useState<boolean>(false);
const queryBatchUpdateStatus = async (ids: React.Key[], status: StatusEnum) => {
if (Array.isArray(ids) && ids.length === 0) {
@@ -268,31 +260,7 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
},
};
const dropdownButtonItems = [
{
key: 'batchStart',
label: '批量启用',
},
{
key: 'batchStop',
label: '批量停用',
},
{
key: 'batchDelete',
label: (
<Popconfirm
title="确定批量删除吗?"
onConfirm={() => {
queryBatchUpdateStatus(selectedRowKeys, StatusEnum.DELETED);
}}
>
<a></a>
</Popconfirm>
),
},
];
const onMenuClick = ({ key }: { key: string }) => {
const onMenuClick = (key: string) => {
switch (key) {
case 'batchStart':
queryBatchUpdateStatus(selectedRowKeys, StatusEnum.ONLINE);
@@ -305,6 +273,27 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
}
};
const downloadMetricQuery = async (
ids: React.Key[],
dateStringList: string[],
pickerType: string,
) => {
if (Array.isArray(ids) && ids.length > 0) {
setDownloadLoading(true);
const [startDate, endDate] = dateStringList;
await batchDownloadMetric({
metricIds: ids,
dateInfo: {
dateMode: 'BETWEEN',
startDate,
endDate,
period: pickerType.toUpperCase(),
},
});
setDownloadLoading(false);
}
};
return (
<>
<ProTable
@@ -350,12 +339,17 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
>
</Button>,
<Dropdown.Button
<BatchCtrlDropDownButton
key="ctrlBtnList"
menu={{ items: dropdownButtonItems, onClick: onMenuClick }}
>
</Dropdown.Button>,
downloadLoading={downloadLoading}
onDeleteConfirm={() => {
queryBatchUpdateStatus(selectedRowKeys, StatusEnum.DELETED);
}}
onMenuClick={onMenuClick}
onDownloadDateRangeChange={(searchDateRange, pickerType) => {
downloadMetricQuery(selectedRowKeys, searchDateRange, pickerType);
}}
/>,
]}
/>
{createModalVisible && (

View File

@@ -179,32 +179,33 @@ const DomainListTree: FC<DomainListProps> = ({
return (
<div className={styles.domainList}>
<Row>
<Col flex="1 1 auto">
{/* <Space> */}
<Search
allowClear
className={styles.search}
placeholder="请输入主题域名称进行查询"
onSearch={onSearch}
/>
{/* </Space> */}
</Col>
{createDomainBtnVisible && (
<Col flex="0 0 40px" style={{ display: 'flex', alignItems: 'center' }}>
<Tooltip title="新增顶级域">
<Button
type="primary"
icon={<PlusOutlined />}
size="small"
onClick={() => {
setProjectInfoParams({ type: 'top', modelType: 'add' });
setProjectInfoModalVisible(true);
onCreateDomainBtnClick?.();
}}
/>
</Tooltip>
{/* <Tooltip title="新增顶级域">
<div className={styles.searchContainer}>
<Row>
<Col flex="1 1 auto">
{/* <Space> */}
<Search
allowClear
className={styles.search}
placeholder="请输入主题域名称进行查询"
onSearch={onSearch}
/>
{/* </Space> */}
</Col>
{createDomainBtnVisible && (
<Col flex="0 0 40px" style={{ display: 'flex', alignItems: 'center' }}>
<Tooltip title="新增顶级域">
<Button
type="primary"
icon={<PlusOutlined />}
size="small"
onClick={() => {
setProjectInfoParams({ type: 'top', modelType: 'add' });
setProjectInfoModalVisible(true);
onCreateDomainBtnClick?.();
}}
/>
</Tooltip>
{/* <Tooltip title="新增顶级域">
<PlusCircleOutlined
onClick={() => {
setProjectInfoParams({ type: 'top', modelType: 'add' });
@@ -214,10 +215,10 @@ const DomainListTree: FC<DomainListProps> = ({
className={styles.addBtn}
/>
</Tooltip> */}
</Col>
)}
</Row>
</Col>
)}
</Row>
</div>
<Tree
expandedKeys={expandedKeys}
onExpand={handleExpand}

View File

@@ -1,6 +1,6 @@
import { Tabs, Button } from 'antd';
import { Tabs, Breadcrumb, Space } from 'antd';
import React from 'react';
import { connect } from 'umi';
import { connect, history } from 'umi';
import ClassDataSourceTable from './ClassDataSourceTable';
import ClassDimensionTable from './ClassDimensionTable';
@@ -11,7 +11,7 @@ import ChatSettingSection from '../ChatSetting/ChatSettingSection';
import OverView from './OverView';
import styles from './style.less';
import type { StateType } from '../model';
import { LeftOutlined } from '@ant-design/icons';
import { HomeOutlined, FundViewOutlined } from '@ant-design/icons';
import { ISemantic } from '../data';
import SemanticGraphCanvas from '../SemanticGraphCanvas';
import RecommendedQuestionsSection from '../components/Entity/RecommendedQuestionsSection';
@@ -39,7 +39,9 @@ const DomainManagerTab: React.FC<Props> = ({
onMenuChange,
}) => {
const defaultTabKey = 'xflow';
const { selectDomainId, domainList, selectModelId } = domainManger;
const { selectDomainId, domainList, selectModelId, selectModelName, selectDomainName } =
domainManger;
const tabItem = [
{
label: '模型管理',
@@ -126,40 +128,49 @@ const DomainManagerTab: React.FC<Props> = ({
return (
<>
<Breadcrumb
className={styles.breadcrumb}
separator=""
items={[
{
path: `/webapp/model/${selectDomainId}/0/overview`,
title: (
<Space
onClick={() => {
onBackDomainBtnClick?.();
}}
style={selectModelName ? {} : { color: '#296df3', fontWeight: 'bold' }}
>
<HomeOutlined />
<span>{selectDomainName}</span>
</Space>
),
},
{
type: 'separator',
separator: selectModelName ? '/' : '',
},
{
title: selectModelName ? (
<Space
onClick={() => {
history.push(`/model/${selectDomainId}/${selectModelId}/`);
}}
style={{ color: '#296df3' }}
>
<FundViewOutlined style={{ position: 'relative', top: '2px' }} />
<span>{selectModelName}</span>
</Space>
) : undefined,
},
]}
/>
<Tabs
className={styles.tab}
items={!isModel ? tabItem : isModelItem}
activeKey={activeKey || defaultTabKey}
destroyInactiveTabPane
size="large"
tabBarExtraContent={{
left: (
<>
{!!selectModelId && (
<div
className={styles.backBtn}
onClick={() => {
onBackDomainBtnClick?.();
}}
>
<LeftOutlined />
</div>
)}
</>
),
// right: isModel ? (
// <Button
// type="primary"
// icon={<LeftOutlined />}
// onClick={() => {
// onBackDomainBtnClick?.();
// }}
// style={{ marginRight: 10, marginBottom: 5 }}
// >
// 返回主题域
// </Button>
// ) : undefined,
}}
onChange={(menuKey: string) => {
onMenuChange?.(menuKey);
}}

View File

@@ -455,7 +455,7 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
forceRender
width={1300}
style={{ top: 48 }}
bodyStyle={{ padding: '32px 40px 48px' }}
styles={{ padding: '32px 40px 48px' }}
destroyOnClose
title={`${isEdit ? '编辑' : '新建'}指标`}
maskClosable={false}

View File

@@ -96,7 +96,7 @@ const ModelCreateFormModal: React.FC<ModelCreateFormModalProps> = (props) => {
return (
<Modal
width={640}
bodyStyle={{ padding: '32px 40px 48px' }}
styles={{ padding: '32px 40px 48px' }}
destroyOnClose
title={'模型信息'}
open={true}

View File

@@ -192,24 +192,6 @@ const PermissionTable: React.FC<Props> = ({ domainManger }) => {
>
</a>
{/* <a
key="dimensionEditBtn"
onClick={() => {
setPermissonData(record);
setDimensionModalVisible(true);
}}
>
维度授权
</a>
<a
key="metricEditBtn"
onClick={() => {
setPermissonData(record);
setMetricModalVisible(true);
}}
>
指标授权
</a> */}
<Popconfirm
title="确认删除?"
okText="是"

View File

@@ -62,7 +62,7 @@ const ProjectInfoForm: React.FC<ProjectInfoFormProps> = (props) => {
return (
<Modal
width={640}
bodyStyle={{ padding: '32px 40px 48px' }}
styles={{ padding: '32px 40px 48px' }}
destroyOnClose
title={titleRender()}
open={true}

View File

@@ -2,13 +2,12 @@
display: flex;
flex-direction: row;
background-color: #fff;
height: calc(100vh - 48px);
height: calc(100vh - 56px);
.projectManger {
width: 100%;
min-height: calc(100vh - 48px);
// background: #f8f9fb;
min-height: calc(100vh - 56px);
background-color: #fff;
display: flex;
position: relative;
@@ -126,6 +125,8 @@
// }
// }
.tab {
border-top: 1px solid #eee;
margin-top: 10px;
:global {
.ant-tabs-tab-btn {
font-size: 16px;
@@ -186,31 +187,18 @@
.domainList {
display: flex;
flex-direction: column;
// width: 400px;
width: 100%;
overflow: hidden;
.searchContainer {
padding:3px 0;
border-bottom: 1px solid #eee;
}
}
// .user {
// display: grid;
// }
// .search{
// width: 50%;
// margin-bottom: 20px;
// }
// .authBtn{
// cursor: pointer;
// }
.classTable {
:global {
.ant-pro-table-search-query-filter {
// padding-left: 0 !important;
margin-bottom: 0;
}
.ant-pro-table-list-toolbar-container {
@@ -335,4 +323,17 @@
padding: 0;
}
}
}
.breadcrumb{
font-size: 18px;
margin: 20px 0 0 20px;
:global {
.ant-breadcrumb-link {
height: 28px;
}
.anticon {
font-size: 18px;
}
}
}

View File

@@ -8,15 +8,15 @@ export enum SENSITIVE_LEVEL {
export const SENSITIVE_LEVEL_OPTIONS = [
{
label: '',
label: '普通',
value: SENSITIVE_LEVEL.LOW,
},
{
label: '',
label: '重要',
value: SENSITIVE_LEVEL.MID,
},
{
label: '',
label: '核心',
value: SENSITIVE_LEVEL.HIGH,
},
];

View File

@@ -202,6 +202,7 @@ export declare namespace ISemantic {
dataFormat: string;
alias: string;
useCnt: number;
isCollect: boolean;
relateDimension?: IRelateDimension;
}

View File

@@ -476,3 +476,17 @@ export async function queryStruct({
return response;
}
}
export function metricStarState(data: { id: number; state: boolean }): Promise<any> {
const { id, state } = data;
if (state) {
return request(`${process.env.API_BASE_URL}collect/createCollectionIndicators`, {
method: 'POST',
data: { id },
});
} else {
return request(`${process.env.API_BASE_URL}collect/deleteCollectionIndicators/${id}`, {
method: 'DELETE',
});
}
}

View File

@@ -1,16 +1,34 @@
import styles from './style.less';
import { Button, Form, Input, InputNumber, message, Space, Switch, Divider } from 'antd';
import {
Button,
Form,
Input,
InputNumber,
message,
Space,
Switch,
Select,
Divider,
Anchor,
Row,
Col,
} from 'antd';
import React, { useState, useEffect } from 'react';
import { getSystemConfig, saveSystemConfig } from '@/services/user';
import ProCard from '@ant-design/pro-card';
import SelectTMEPerson from '@/components/SelectTMEPerson';
import { SystemConfigParametersItem, SystemConfig } from './types';
import { groupBy } from 'lodash';
const FormItem = Form.Item;
const { TextArea } = Input;
const System: React.FC = () => {
const [systemConfig, setSystemConfig] = useState<SystemConfigParametersItem[]>([]);
const [systemConfig, setSystemConfig] = useState<Record<string, SystemConfigParametersItem[]>>(
{},
);
const [anchorItems, setAnchorItems] = useState<{ key: string; href: string; title: string }[]>(
[],
);
const [configSource, setConfigSource] = useState<SystemConfig>();
useEffect(() => {
@@ -21,7 +39,16 @@ const System: React.FC = () => {
const { code, data, msg } = await getSystemConfig();
if (code === 200) {
const { parameters, admins } = data;
setSystemConfig(parameters);
const groupByConfig = groupBy(parameters, 'module');
const anchor = Object.keys(groupByConfig).map((key: string) => {
return {
key,
href: `#${key}`,
title: key,
};
});
setAnchorItems(anchor);
setSystemConfig(groupByConfig);
setInitData(admins, parameters);
setConfigSource(data);
} else {
@@ -29,8 +56,8 @@ const System: React.FC = () => {
}
};
const setInitData = (admins: string[], systemConfig: SystemConfigParametersItem[]) => {
const fieldsValue = systemConfig.reduce(
const setInitData = (admins: string[], systemConfigParameters: SystemConfigParametersItem[]) => {
const fieldsValue = systemConfigParameters.reduce(
(fields, item) => {
const { name, value } = item;
return {
@@ -48,7 +75,7 @@ const System: React.FC = () => {
const { code, msg } = await saveSystemConfig({
...configSource,
admins: submitData.admins,
parameters: systemConfig.map((item) => {
parameters: configSource!.parameters.map((item) => {
const { name } = item;
if (submitData[name] !== undefined) {
return {
@@ -68,68 +95,99 @@ const System: React.FC = () => {
return (
<>
<div style={{ margin: '40px auto', width: 800 }}>
<ProCard
title="系统设置"
extra={
<Space>
<Button
type="primary"
onClick={() => {
querySaveSystemConfig();
}}
>
</Button>
</Space>
}
>
<Form form={form} layout="vertical" className={styles.form}>
<FormItem name="admins" label="管理员">
<SelectTMEPerson placeholder="请邀请团队成员" />
</FormItem>
<Divider />
{systemConfig.map((item: SystemConfigParametersItem) => {
const { dataType, name, comment } = item;
let defaultItem = <Input />;
switch (dataType) {
case 'string':
defaultItem = <TextArea placeholder="" style={{ height: 100 }} />;
break;
case 'number':
defaultItem = <InputNumber style={{ width: '100%' }} />;
break;
case 'bool':
return (
<FormItem
name={name}
label={comment}
key={name}
valuePropName="checked"
getValueFromEvent={(value) => {
return value === true ? 'true' : 'false';
}}
getValueProps={(value) => {
return {
checked: value === 'true',
};
}}
>
<Switch />
</FormItem>
);
default:
defaultItem = <Input />;
break;
<div style={{ margin: '40px auto', width: 1200 }}>
<Row>
<Col span={18}>
<ProCard
title="系统设置"
extra={
<Space>
<Button
type="primary"
onClick={() => {
querySaveSystemConfig();
}}
>
</Button>
</Space>
}
return (
<FormItem name={name} label={comment} key={name}>
{defaultItem}
>
<Form form={form} layout="vertical" className={styles.form}>
<FormItem name="admins" label="管理员">
<SelectTMEPerson placeholder="请邀请团队成员" />
</FormItem>
);
})}
</Form>
</ProCard>
<Divider />
<Space direction="vertical" style={{ width: '100%' }} size={35}>
{Object.keys(systemConfig).map((key: string) => {
const itemList = systemConfig[key];
return (
<ProCard
title={<span style={{ color: '#296df3' }}>{key}</span>}
key={key}
bordered
id={key}
>
{itemList.map((item) => {
const { dataType, name, comment } = item;
let defaultItem = <Input />;
switch (dataType) {
case 'string':
defaultItem = <TextArea placeholder="" style={{ height: 100 }} />;
break;
case 'number':
defaultItem = <InputNumber style={{ width: '100%' }} />;
break;
case 'bool':
return (
<FormItem
name={name}
label={comment}
key={name}
valuePropName="checked"
getValueFromEvent={(value) => {
return value === true ? 'true' : 'false';
}}
getValueProps={(value) => {
return {
checked: value === 'true',
};
}}
>
<Switch />
</FormItem>
);
case 'list': {
const { candidateValues } = item;
const options = candidateValues.map((value) => {
return { label: value, value };
});
defaultItem = <Select style={{ width: '100%' }} options={options} />;
break;
}
default:
defaultItem = <Input />;
break;
}
return (
<FormItem name={name} label={comment} key={name}>
{defaultItem}
</FormItem>
);
})}
</ProCard>
);
})}
</Space>
</Form>
</ProCard>
</Col>
<Col span={6} style={{ background: '#fff' }}>
<div style={{ marginTop: 20 }}>
<Anchor items={anchorItems} />
</div>
</Col>
</Row>
</div>
</>
);

View File

@@ -3,6 +3,7 @@ export type SystemConfigParametersItem = {
name: string;
comment: string;
value: string;
candidateValues: string[];
};
export type SystemConfig = {