├── mock ├── .gitkeep ├── login.js ├── projects.js └── persona.js ├── __mocks__ ├── fileMock.js ├── umi │ ├── link.js │ ├── redirect.js │ ├── withRouter.js │ └── router.js └── shortid.js ├── renovate.json ├── .env ├── src ├── styles │ ├── index.less │ ├── core │ │ ├── index.less │ │ ├── motion │ │ │ ├── other.less │ │ │ ├── fade.less │ │ │ └── swing.less │ │ └── motion.less │ ├── mixins │ │ ├── card.less │ │ ├── size.less │ │ ├── index.less │ │ ├── reset.less │ │ ├── clearfix.less │ │ ├── compatibility.less │ │ ├── motion.less │ │ └── iconfont.less │ └── themes │ │ ├── theme.js │ │ └── override.less ├── assets │ ├── logo.png │ ├── loginBg.png │ ├── persona.png │ └── photos │ │ ├── females │ │ ├── 1.png │ │ ├── 2.png │ │ ├── 3.png │ │ ├── 4.png │ │ ├── 5.png │ │ ├── 6.png │ │ ├── 7.png │ │ ├── 8.png │ │ ├── 9.png │ │ ├── 10.png │ │ └── index.ts │ │ ├── males │ │ ├── 1.png │ │ ├── 2.jpg │ │ ├── 3.jpg │ │ ├── 4.png │ │ └── index.ts │ │ └── index.ts ├── components │ ├── Charts │ │ ├── TimelineChart │ │ │ ├── index.less │ │ │ └── index.d.ts │ │ ├── TagCloud │ │ │ ├── index.less │ │ │ └── index.d.ts │ │ ├── Field │ │ │ ├── index.d.ts │ │ │ ├── index.js │ │ │ └── index.less │ │ ├── demo │ │ │ ├── mini-progress.md │ │ │ ├── gauge.md │ │ │ ├── mini-pie.md │ │ │ ├── waterwave.md │ │ │ ├── bar.md │ │ │ ├── tag-cloud.md │ │ │ ├── mini-area.md │ │ │ ├── mini-bar.md │ │ │ ├── timeline-chart.md │ │ │ ├── pie.md │ │ │ ├── radar.md │ │ │ └── chart-card.md │ │ ├── g2.js │ │ ├── WaterWave │ │ │ ├── index.d.ts │ │ │ └── index.less │ │ ├── Gauge │ │ │ └── index.d.ts │ │ ├── MiniBar │ │ │ ├── index.d.ts │ │ │ └── index.js │ │ ├── index.less │ │ ├── Bar │ │ │ └── index.d.ts │ │ ├── ChartCard │ │ │ ├── index.d.ts │ │ │ ├── index.less │ │ │ └── index.js │ │ ├── Radar │ │ │ ├── index.d.ts │ │ │ └── index.less │ │ ├── MiniArea │ │ │ └── index.d.ts │ │ ├── index.d.ts │ │ ├── index.js │ │ ├── MiniProgress │ │ │ ├── index.less │ │ │ └── index.tsx │ │ ├── autoHeight.js │ │ └── Pie │ │ │ └── index.less │ ├── DescriptionList │ │ ├── responsive.js │ │ ├── index.js │ │ ├── Description.d.ts │ │ ├── index.d.ts │ │ ├── Description.js │ │ ├── DescriptionList.js │ │ └── index.less │ ├── Authorized │ │ ├── Authorized.js │ │ ├── index.js │ │ ├── AuthorizedRoute.d.ts │ │ ├── demo │ │ │ ├── basic.md │ │ │ ├── secured.md │ │ │ ├── AuthorizedArray.md │ │ │ └── AuthorizedFunction.md │ │ ├── AuthorizedRoute.js │ │ ├── renderAuthorize.js │ │ ├── index.d.ts │ │ ├── PromiseRender.js │ │ └── Secured.js │ ├── PageLoading │ │ └── index.tsx │ ├── Ellipsis │ │ └── index.less │ ├── Login │ │ ├── LoginSubmit.js │ │ ├── LoginTab.js │ │ ├── index.d.ts │ │ ├── index.less │ │ └── map.js │ ├── GlobalFooter │ │ ├── index.less │ │ └── index.tsx │ ├── index.ts │ ├── Header │ │ ├── HeaderSearch.less │ │ └── index.less │ ├── ContextRightMenu │ │ ├── index.less │ │ └── index.tsx │ └── SiderMenu │ │ ├── index.test.tsx │ │ └── styles.less ├── pages │ ├── index.less │ ├── Interview │ │ ├── components │ │ │ ├── RecordList │ │ │ │ ├── Editor │ │ │ │ │ ├── handlers │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── onEnter.ts │ │ │ │ │ ├── validation │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── validateNode.ts │ │ │ │ │ ├── changes │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── splitListItem.ts │ │ │ │ │ │ └── wrapInList.ts │ │ │ │ │ ├── utils │ │ │ │ │ │ ├── isList.ts │ │ │ │ │ │ ├── isSelectionInList.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── getCurrentList.ts │ │ │ │ │ │ ├── getListForItem.ts │ │ │ │ │ │ ├── getCurrentItem.ts │ │ │ │ │ │ ├── getPreviousItem.ts │ │ │ │ │ │ └── getItemsAtRange.ts │ │ │ │ │ ├── options.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── core.ts │ │ │ │ ├── index.less │ │ │ │ ├── PopupMenu.less │ │ │ │ ├── InputTooltip.test.tsx │ │ │ │ ├── PopupMenu.test.tsx │ │ │ │ ├── index.test.tsx │ │ │ │ └── PopupMenu.tsx │ │ │ ├── index.ts │ │ │ ├── TagGroup │ │ │ │ ├── DraggableTag.less │ │ │ │ ├── tag.less │ │ │ │ ├── index.less │ │ │ │ └── tags.tsx │ │ │ ├── TagSelector │ │ │ │ ├── NewTag.less │ │ │ │ ├── Label.less │ │ │ │ ├── index.less │ │ │ │ ├── EditableTags.less │ │ │ │ └── Label.tsx │ │ │ ├── TagContent │ │ │ │ └── index.test.tsx │ │ │ └── StarPic │ │ │ │ └── index.tsx │ │ ├── index.tsx │ │ └── models │ │ │ ├── record.ts │ │ │ └── interviewDisplay.ts │ ├── Data │ │ ├── index.tsx │ │ ├── components │ │ │ ├── DataPanel │ │ │ │ ├── Upload.less │ │ │ │ ├── ClusterMethod.less │ │ │ │ ├── Charts.less │ │ │ │ ├── RecuceDims.less │ │ │ │ ├── QuestionSelector.less │ │ │ │ ├── index.less │ │ │ │ ├── ReductionOpts.less │ │ │ │ ├── LabelMatch.less │ │ │ │ └── ClusterDim.tsx │ │ │ ├── index.ts │ │ │ ├── ClusterDisplay │ │ │ │ ├── color.ts │ │ │ │ └── index.less │ │ │ ├── LabelSelector │ │ │ │ ├── index.less │ │ │ │ └── index.tsx │ │ │ ├── CorrTable.tsx │ │ │ ├── Plots.tsx │ │ │ ├── VarianceExplain.test.tsx │ │ │ ├── CompMatrixTable.tsx │ │ │ └── VarianceExplain.tsx │ │ ├── cluster.less │ │ ├── index.test.tsx │ │ ├── charts.less │ │ ├── reduction.less │ │ ├── _layout.less │ │ ├── models │ │ │ └── table.ts │ │ ├── _layout.tsx │ │ ├── table.less │ │ ├── analysis.less │ │ ├── cluster.tsx │ │ └── reduction.test.tsx │ ├── User │ │ ├── index.tsx │ │ └── models │ │ │ └── register.ts │ ├── Persona │ │ ├── index.tsx │ │ ├── components │ │ │ ├── index.ts │ │ │ ├── DraggableTag.less │ │ │ ├── PersonaEditor │ │ │ │ ├── PhotoModal.less │ │ │ │ └── DraggableBlock.less │ │ │ ├── DraggableList.less │ │ │ └── DimensionList │ │ │ │ └── index.less │ │ ├── layout.less │ │ ├── edit.less │ │ ├── export.less │ │ └── match.less │ ├── Project │ │ └── index.tsx │ ├── index.tsx │ └── Authorized.tsx ├── utils │ ├── layout.ts │ ├── authority-old.js │ ├── Authorized.ts │ ├── Authorized-old.js │ ├── router.ts │ ├── __test__ │ │ ├── utils.test.ts │ │ ├── visualization.test.ts │ │ ├── charts.test.ts │ │ ├── label.test.ts │ │ └── quesData.test.ts │ ├── index.ts │ ├── label.ts │ ├── authority.ts │ ├── doc.ts │ ├── persona.ts │ ├── menu.ts │ ├── utils.less │ ├── record.ts │ └── charts.ts ├── services │ ├── tag.ts │ ├── persona.ts │ ├── index.ts │ ├── user.ts │ ├── error.ts │ ├── project.ts │ └── api.ts ├── common │ ├── index.ts │ ├── layout.ts │ ├── PageTitle.ts │ ├── menu.ts │ └── persona.ts ├── layouts │ ├── BasicLayout.less │ ├── UserLayout.test.tsx │ ├── UserLayout.less │ ├── index.test.tsx │ ├── BasicLayout.test.tsx │ ├── ProjectLayout.tsx │ ├── UserLayout.tsx │ └── BasicLayout.tsx ├── models │ ├── menu.ts │ ├── __test__ │ │ └── menu.test.ts │ └── interview.ts └── global.less ├── public └── favicon.ico ├── data ├── questionnaries.xlsx ├── userModels.ts ├── clusterResults.ts ├── txt.txt ├── keyDimensions.ts ├── quesData.ts └── records.ts ├── typings ├── externals.d.ts └── dva.d.ts ├── commitlint.config.js ├── .prettierrc.js ├── .editorconfig ├── README.md ├── jest.config.js ├── SECURITY.md ├── tsconfig.json ├── .stylelintrc ├── .gitlab-ci.yml ├── .umirc.js ├── .gitattributes └── .gitignore /mock/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /__mocks__/fileMock.js: -------------------------------------------------------------------------------- 1 | export default {}; 2 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true 2 | BROWSER=none 3 | ESLINT=none 4 | -------------------------------------------------------------------------------- /__mocks__/umi/link.js: -------------------------------------------------------------------------------- 1 | import { spy } from 'sinon'; 2 | 3 | export default {}; 4 | -------------------------------------------------------------------------------- /src/styles/index.less: -------------------------------------------------------------------------------- 1 | @import "./themes/default"; 2 | @import "./core/index"; 3 | -------------------------------------------------------------------------------- /__mocks__/umi/redirect.js: -------------------------------------------------------------------------------- 1 | export default (props) =>
{props.children}
; 2 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arvinxx/lingmao/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arvinxx/lingmao/HEAD/src/assets/logo.png -------------------------------------------------------------------------------- /src/assets/loginBg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arvinxx/lingmao/HEAD/src/assets/loginBg.png -------------------------------------------------------------------------------- /src/assets/persona.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arvinxx/lingmao/HEAD/src/assets/persona.png -------------------------------------------------------------------------------- /src/components/Charts/TimelineChart/index.less: -------------------------------------------------------------------------------- 1 | .timelineChart { 2 | background: #fff; 3 | } 4 | -------------------------------------------------------------------------------- /__mocks__/umi/withRouter.js: -------------------------------------------------------------------------------- 1 | import { spy } from 'sinon'; 2 | 3 | export default (props) => props; 4 | -------------------------------------------------------------------------------- /data/questionnaries.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arvinxx/lingmao/HEAD/data/questionnaries.xlsx -------------------------------------------------------------------------------- /src/pages/index.less: -------------------------------------------------------------------------------- 1 | .container { 2 | height: 20%; 3 | margin: 5rem; 4 | padding: 5rem; 5 | } 6 | -------------------------------------------------------------------------------- /__mocks__/umi/router.js: -------------------------------------------------------------------------------- 1 | import { spy } from 'sinon'; 2 | 3 | export default { 4 | push: spy(), 5 | }; 6 | -------------------------------------------------------------------------------- /typings/externals.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.less'; 2 | declare module '*.png'; 3 | declare module '*.jpg'; 4 | -------------------------------------------------------------------------------- /__mocks__/shortid.js: -------------------------------------------------------------------------------- 1 | export default { 2 | characters: () => {}, 3 | generate: () => 'testKey', 4 | }; 5 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['./node_modules/commitlint-config-gitmoji'], 3 | }; 4 | -------------------------------------------------------------------------------- /mock/login.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'post /api/v1/login': {userInfo: {}, isValid: true}, 3 | }; 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/photos/females/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arvinxx/lingmao/HEAD/src/assets/photos/females/1.png -------------------------------------------------------------------------------- /src/assets/photos/females/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arvinxx/lingmao/HEAD/src/assets/photos/females/2.png -------------------------------------------------------------------------------- /src/assets/photos/females/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arvinxx/lingmao/HEAD/src/assets/photos/females/3.png -------------------------------------------------------------------------------- /src/assets/photos/females/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arvinxx/lingmao/HEAD/src/assets/photos/females/4.png -------------------------------------------------------------------------------- /src/assets/photos/females/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arvinxx/lingmao/HEAD/src/assets/photos/females/5.png -------------------------------------------------------------------------------- /src/assets/photos/females/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arvinxx/lingmao/HEAD/src/assets/photos/females/6.png -------------------------------------------------------------------------------- /src/assets/photos/females/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arvinxx/lingmao/HEAD/src/assets/photos/females/7.png -------------------------------------------------------------------------------- /src/assets/photos/females/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arvinxx/lingmao/HEAD/src/assets/photos/females/8.png -------------------------------------------------------------------------------- /src/assets/photos/females/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arvinxx/lingmao/HEAD/src/assets/photos/females/9.png -------------------------------------------------------------------------------- /src/assets/photos/males/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arvinxx/lingmao/HEAD/src/assets/photos/males/1.png -------------------------------------------------------------------------------- /src/assets/photos/males/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arvinxx/lingmao/HEAD/src/assets/photos/males/2.jpg -------------------------------------------------------------------------------- /src/assets/photos/males/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arvinxx/lingmao/HEAD/src/assets/photos/males/3.jpg -------------------------------------------------------------------------------- /src/assets/photos/males/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arvinxx/lingmao/HEAD/src/assets/photos/males/4.png -------------------------------------------------------------------------------- /src/assets/photos/females/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arvinxx/lingmao/HEAD/src/assets/photos/females/10.png -------------------------------------------------------------------------------- /src/pages/Interview/components/RecordList/Editor/handlers/index.ts: -------------------------------------------------------------------------------- 1 | export { default as onEnter } from './onEnter'; 2 | -------------------------------------------------------------------------------- /src/utils/layout.ts: -------------------------------------------------------------------------------- 1 | export const getPageTitle = (pathname, title) => { 2 | return `${pathname} - ${title}`; 3 | }; 4 | -------------------------------------------------------------------------------- /src/assets/photos/index.ts: -------------------------------------------------------------------------------- 1 | export { default as females } from './females'; 2 | export { default as males } from './males'; 3 | -------------------------------------------------------------------------------- /src/styles/core/index.less: -------------------------------------------------------------------------------- 1 | @import "../mixins/index"; 2 | @import "./base"; 3 | @import "./iconfont"; 4 | @import "./motion"; 5 | -------------------------------------------------------------------------------- /src/styles/mixins/card.less: -------------------------------------------------------------------------------- 1 | .base-card { 2 | background: @base-white; 3 | box-shadow: @shadow-1; 4 | border-radius: 4px; 5 | } 6 | -------------------------------------------------------------------------------- /typings/dva.d.ts: -------------------------------------------------------------------------------- 1 | export type DvaModel = { 2 | state: T; 3 | reducers: any; 4 | effects?: any; 5 | subscriptions?: any; 6 | }; 7 | -------------------------------------------------------------------------------- /src/services/tag.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | export const queryTags = async () => { 4 | return await axios.get('/api/tags'); 5 | }; 6 | -------------------------------------------------------------------------------- /src/pages/Data/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Redirect from 'umi/redirect'; 3 | 4 | export default () => ; 5 | -------------------------------------------------------------------------------- /src/pages/User/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Redirect from 'umi/redirect'; 3 | 4 | export default () => ; 5 | -------------------------------------------------------------------------------- /src/pages/Persona/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Redirect from 'umi/redirect'; 3 | 4 | export default () => ; 5 | -------------------------------------------------------------------------------- /src/pages/Project/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Redirect from 'umi/redirect'; 3 | 4 | export default () => ; 5 | -------------------------------------------------------------------------------- /src/pages/Interview/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Redirect from 'umi/redirect'; 3 | 4 | export default () => ; 5 | -------------------------------------------------------------------------------- /src/services/persona.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | export const queryPersonaModels = async () => { 4 | return await axios.get('/api/persona'); 5 | }; 6 | -------------------------------------------------------------------------------- /src/common/index.ts: -------------------------------------------------------------------------------- 1 | export * from './menu'; 2 | export * from './header'; 3 | export * from './layout'; 4 | export * from './persona'; 5 | export * from './PageTitle'; 6 | -------------------------------------------------------------------------------- /src/components/Charts/TagCloud/index.less: -------------------------------------------------------------------------------- 1 | .tagCloud { 2 | overflow: hidden; 3 | canvas { 4 | transform: scale(0.25); 5 | transform-origin: 0 0; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/pages/Data/components/DataPanel/Upload.less: -------------------------------------------------------------------------------- 1 | @import '~styles/themes/default'; 2 | 3 | .button { 4 | display: flex; 5 | justify-content: space-between; 6 | } 7 | -------------------------------------------------------------------------------- /src/common/layout.ts: -------------------------------------------------------------------------------- 1 | export const UserLayout: Array = ['/user/login', '/user/register']; 2 | export const ProjectLayout: Array = ['/project', '/project/view']; 3 | -------------------------------------------------------------------------------- /src/pages/Interview/components/RecordList/Editor/validation/index.ts: -------------------------------------------------------------------------------- 1 | export { default as schema } from './schema'; 2 | export { default as validateNode } from './validateNode'; 3 | -------------------------------------------------------------------------------- /src/pages/Data/components/DataPanel/ClusterMethod.less: -------------------------------------------------------------------------------- 1 | .info { 2 | margin: 8px 0 12px 0; 3 | display: flex; 4 | color: @text-color-secondary; 5 | align-items: baseline; 6 | } 7 | -------------------------------------------------------------------------------- /src/pages/Interview/components/RecordList/Editor/changes/index.ts: -------------------------------------------------------------------------------- 1 | export { default as wrapInList } from './wrapInList'; 2 | export { default as splitListItem } from './splitListItem'; 3 | -------------------------------------------------------------------------------- /src/services/index.ts: -------------------------------------------------------------------------------- 1 | export * from './api'; 2 | export * from './tag'; 3 | export * from './project'; 4 | export * from './user'; 5 | export * from './project'; 6 | export * from './ml'; 7 | -------------------------------------------------------------------------------- /src/components/DescriptionList/responsive.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 1: { xs: 24 }, 3 | 2: { xs: 24, sm: 12 }, 4 | 3: { xs: 24, sm: 12, md: 8 }, 5 | 4: { xs: 24, sm: 12, md: 6 }, 6 | }; 7 | -------------------------------------------------------------------------------- /src/styles/mixins/size.less: -------------------------------------------------------------------------------- 1 | // Sizing shortcuts 2 | 3 | .size(@width; @height) { 4 | width: @width; 5 | height: @height; 6 | } 7 | 8 | .square(@size) { 9 | .size(@size; @size); 10 | } 11 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // 配置目的:用于配合 Airbnb 的 ESlint 规则 3 | singleQuote: true, // 使用单引号而不是双引号 4 | trailingComma: 'es5', // 在所以支持的地方最后面加上分号 5 | printWidth: 100, 6 | arrowParens: 'always', 7 | }; 8 | -------------------------------------------------------------------------------- /src/assets/photos/males/index.ts: -------------------------------------------------------------------------------- 1 | import photo1 from './1.png'; 2 | import photo2 from './2.jpg'; 3 | import photo3 from './3.jpg'; 4 | import photo4 from './4.png'; 5 | 6 | export default [photo1, photo2, photo3, photo4]; 7 | -------------------------------------------------------------------------------- /src/components/DescriptionList/index.js: -------------------------------------------------------------------------------- 1 | import DescriptionList from './DescriptionList'; 2 | import Description from './Description'; 3 | 4 | DescriptionList.Description = Description; 5 | export default DescriptionList; 6 | -------------------------------------------------------------------------------- /src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Redirect from 'umi/redirect'; 3 | 4 | export default class Index extends Component { 5 | render() { 6 | return ; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/pages/Data/cluster.less: -------------------------------------------------------------------------------- 1 | @import '../../styles/themes/default'; 2 | 3 | .container { 4 | padding: 24px; 5 | display: flex; 6 | height: 250px; 7 | justify-content: center; 8 | flex-wrap: wrap; 9 | width: 100%; 10 | } 11 | -------------------------------------------------------------------------------- /src/pages/Data/index.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { shallow } from 'enzyme'; 3 | import App from './index'; 4 | 5 | it('should render', () => { 6 | const wrapper = shallow(); 7 | expect(wrapper).toMatchSnapshot(); 8 | }); 9 | -------------------------------------------------------------------------------- /src/styles/core/motion/other.less: -------------------------------------------------------------------------------- 1 | @keyframes loadingCircle { 2 | 0% { 3 | transform-origin: 50% 50%; 4 | transform: rotate(0deg); 5 | } 6 | 100% { 7 | transform-origin: 50% 50%; 8 | transform: rotate(360deg); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/components/DescriptionList/Description.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default class Description extends React.Component< 4 | { 5 | term: React.ReactNode; 6 | style?: React.CSSProperties; 7 | }, 8 | any 9 | > {} 10 | -------------------------------------------------------------------------------- /src/layouts/BasicLayout.less: -------------------------------------------------------------------------------- 1 | @import '../styles/index'; 2 | 3 | .layout { 4 | transition: padding-left @animation-duration-base; 5 | :global { 6 | .ant-layout-content { 7 | //display: block; 8 | //min-height: 100%; 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/styles/mixins/index.less: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------------------------------- 3 | @import './size'; 4 | @import './compatibility'; 5 | @import './clearfix'; 6 | @import './iconfont'; 7 | @import './motion'; 8 | @import './reset'; 9 | @import './card'; 10 | -------------------------------------------------------------------------------- /src/components/Charts/Field/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | export interface FieldProps { 3 | label: React.ReactNode; 4 | value: React.ReactNode; 5 | style?: React.CSSProperties; 6 | } 7 | 8 | export default class Field extends React.Component {} 9 | -------------------------------------------------------------------------------- /src/pages/Persona/components/index.ts: -------------------------------------------------------------------------------- 1 | export { default as DraggableTag } from './DraggableTag'; 2 | export { default as DraggableList } from './DraggableList'; 3 | export { default as PersonaEditor } from './PersonaEditor'; 4 | export { default as DimensionList } from './DimensionList'; 5 | -------------------------------------------------------------------------------- /src/styles/themes/theme.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs'); 3 | const lessToJs = require('less-vars-to-js'); 4 | 5 | const themeVariables = lessToJs(fs.readFileSync(path.join(__dirname, './override.less'), 'utf8')); 6 | 7 | module.exports = themeVariables; 8 | -------------------------------------------------------------------------------- /src/components/Charts/demo/mini-progress.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 3 3 | title: 迷你进度条 4 | --- 5 | 6 | ````jsx 7 | import { MiniProgress } from 'ant-design-pro/lib/Charts'; 8 | 9 | ReactDOM.render( 10 | 11 | , mountNode); 12 | ```` 13 | -------------------------------------------------------------------------------- /src/components/Charts/g2.js: -------------------------------------------------------------------------------- 1 | // 全局 G2 设置 2 | import { track, setTheme } from 'bizcharts'; 3 | 4 | track(false); 5 | 6 | const config = { 7 | defaultColor: '#439afc', 8 | shape: { 9 | interval: { 10 | fillOpacity: 1, 11 | }, 12 | }, 13 | }; 14 | 15 | setTheme(config); 16 | -------------------------------------------------------------------------------- /src/pages/Persona/components/DraggableTag.less: -------------------------------------------------------------------------------- 1 | @import '~styles/themes/default'; 2 | 3 | .dragging { 4 | opacity: 0.5; 5 | background: @white-1; 6 | } 7 | 8 | .drop-right { 9 | //border-right: 2px dashed @blue-3; 10 | } 11 | .drop-left { 12 | //border-left: 2px dashed @blue-3; 13 | } 14 | -------------------------------------------------------------------------------- /src/pages/Interview/components/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Upload } from './Upload'; 2 | export { default as TagContent } from './TagContent'; 3 | export { default as TagSelector } from './TagSelector'; 4 | export { default as RecordList } from './RecordList'; 5 | export { default as StarPic } from './StarPic'; 6 | -------------------------------------------------------------------------------- /src/styles/mixins/reset.less: -------------------------------------------------------------------------------- 1 | @import '../themes/default'; 2 | 3 | .reset-component() { 4 | font-family: @font-family; 5 | font-size: @font-size-base; 6 | line-height: @line-height-base; 7 | color: @text-color; 8 | box-sizing: border-box; 9 | margin: 0; 10 | padding: 0; 11 | list-style: none; 12 | } 13 | -------------------------------------------------------------------------------- /src/components/Charts/Field/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import styles from './index.less'; 4 | 5 | const Field = ({ label, value, ...rest }) => ( 6 |
7 | {label} 8 | {value} 9 |
10 | ); 11 | 12 | export default Field; 13 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | 15 | [Makefile] 16 | indent_style = tab 17 | -------------------------------------------------------------------------------- /src/pages/Data/components/DataPanel/Charts.less: -------------------------------------------------------------------------------- 1 | @import '~styles/themes/default'; 2 | 3 | .container { 4 | padding-left: 24px; 5 | } 6 | 7 | .charts { 8 | margin-bottom: 24px; 9 | } 10 | 11 | .notice { 12 | margin-top: 12px; 13 | color: @text-color-secondary; 14 | display: flex; 15 | align-items: baseline; 16 | } 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 灵猫 | 用户研究神器 2 | 3 | [![pipeline status](https://gitlab.com/arvinxx/lingmao-frontend/badges/master/pipeline.svg)](https://gitlab.com/arvinxx/lingmao-frontend/commits/master) 4 | [![coverage report](https://gitlab.com/arvinxx/lingmao-frontend/badges/master/coverage.svg)](https://gitlab.com/arvinxx/lingmao-frontend/commits/master) 5 | -------------------------------------------------------------------------------- /src/pages/Interview/components/RecordList/Editor/utils/isList.ts: -------------------------------------------------------------------------------- 1 | // @flow 2 | import { Node } from 'slate'; 3 | 4 | import Options from '../options'; 5 | 6 | /** 7 | * True if the node is a list container 8 | */ 9 | export default (opts: Options, node: Node): boolean => { 10 | return opts.types.includes(node.type); 11 | }; 12 | -------------------------------------------------------------------------------- /src/styles/mixins/clearfix.less: -------------------------------------------------------------------------------- 1 | // mixins for clearfix 2 | // ------------------------ 3 | .clearfix() { 4 | zoom: 1; 5 | &:before, 6 | &:after { 7 | content: " "; 8 | display: table; 9 | } 10 | &:after { 11 | clear: both; 12 | visibility: hidden; 13 | font-size: 0; 14 | height: 0; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/components/Charts/TagCloud/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | export interface TagCloudProps { 3 | data: Array<{ 4 | name: string; 5 | value: number; 6 | }>; 7 | height: number; 8 | style?: React.CSSProperties; 9 | } 10 | 11 | export default class TagCloud extends React.Component {} 12 | -------------------------------------------------------------------------------- /src/components/Charts/WaterWave/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | export interface WaterWaveProps { 3 | title: React.ReactNode; 4 | color?: string; 5 | height: number; 6 | percent: number; 7 | style?: React.CSSProperties; 8 | } 9 | 10 | export default class WaterWave extends React.Component {} 11 | -------------------------------------------------------------------------------- /src/pages/Data/charts.less: -------------------------------------------------------------------------------- 1 | @import '../../styles/themes/default'; 2 | .base-card { 3 | background: @base-white; 4 | box-shadow: @shadow-1; 5 | border-radius: 4px; 6 | } 7 | 8 | .container { 9 | padding: 24px; 10 | margin: 24px; 11 | .base-card; 12 | min-width: 700px; 13 | max-width: 1200px; 14 | overflow: auto; 15 | } 16 | -------------------------------------------------------------------------------- /src/components/Charts/demo/gauge.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 7 3 | title: 仪表盘 4 | --- 5 | 6 | 仪表盘是一种进度展示方式,可以更直观的展示当前的进展情况,通常也可表示占比。 7 | 8 | ````jsx 9 | import { Gauge } from 'ant-design-pro/lib/Charts'; 10 | 11 | ReactDOM.render( 12 | 17 | , mountNode); 18 | ```` 19 | -------------------------------------------------------------------------------- /src/components/Authorized/Authorized.js: -------------------------------------------------------------------------------- 1 | import CheckPermissions from './CheckPermissions'; 2 | 3 | const Authorized = ({ children, authority, noMatch = null }) => { 4 | const childrenRender = typeof children === 'undefined' ? null : children; 5 | return CheckPermissions(authority, childrenRender, noMatch); 6 | }; 7 | 8 | export default Authorized; 9 | -------------------------------------------------------------------------------- /src/components/Charts/Gauge/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | export interface GaugeProps { 3 | title: React.ReactNode; 4 | color?: string; 5 | height: number; 6 | bgColor?: number; 7 | percent: number; 8 | style?: React.CSSProperties; 9 | } 10 | 11 | export default class Gauge extends React.Component {} 12 | -------------------------------------------------------------------------------- /src/components/PageLoading/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Spin } from 'antd'; 3 | 4 | // loading components from code split 5 | // https://umijs.org/plugin/umi-plugin-react.html#dynamicimport 6 | export default () => ( 7 |
8 | 9 |
10 | ); 11 | -------------------------------------------------------------------------------- /src/utils/authority-old.js: -------------------------------------------------------------------------------- 1 | // use localStorage to store the authority info, which might be sent from server in actual project. 2 | export function getAuthority() { 3 | return localStorage.getItem('antd-pro-authority') || 'admin'; 4 | } 5 | 6 | export function setAuthority(authority) { 7 | return localStorage.setItem('antd-pro-authority', authority); 8 | } 9 | -------------------------------------------------------------------------------- /src/components/Charts/MiniBar/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | export interface MiniBarProps { 3 | color?: string; 4 | height: number; 5 | data: Array<{ 6 | x: number | string; 7 | y: number; 8 | }>; 9 | style?: React.CSSProperties; 10 | } 11 | 12 | export default class MiniBar extends React.Component {} 13 | -------------------------------------------------------------------------------- /src/components/Charts/demo/mini-pie.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 6 3 | title: 迷你饼状图 4 | --- 5 | 6 | 通过简化 `Pie` 属性的设置,可以快速的实现极简的饼状图,可配合 `ChartCard` 组合展 7 | 现更多业务场景。 8 | 9 | ```jsx 10 | import { Pie } from 'ant-design-pro/lib/Charts'; 11 | 12 | ReactDOM.render( 13 | , 14 | mountNode 15 | ); 16 | ``` 17 | -------------------------------------------------------------------------------- /data/userModels.ts: -------------------------------------------------------------------------------- 1 | import { quesData } from './quesData'; 2 | import { TQuesData } from '@/models/data'; 3 | import Mock from 'mockjs'; 4 | const Random = Mock.Random; 5 | 6 | export const userModels: TQuesData = quesData.map((item) => ({ 7 | ...item, 8 | type: Random.natural(1, 5), 9 | typeName: Random.cword(), 10 | percent: Random.natural(1, 40), 11 | })); 12 | -------------------------------------------------------------------------------- /data/clusterResults.ts: -------------------------------------------------------------------------------- 1 | import Mock from 'mockjs'; 2 | 3 | export const clusterResult = () => 4 | Mock.mock({ 5 | 'array|1-5': [ 6 | { 7 | labelKey: '@guid', 8 | 'value|1-5.1': 1, 9 | text: '@ctitle', 10 | labelText: '@ctitle', 11 | }, 12 | ], 13 | }); 14 | 15 | export const clusterArray = [0, 0, 0, 1, 2, 1, 0, 0, 2, 0]; 16 | -------------------------------------------------------------------------------- /src/services/user.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import qs from 'qs'; 3 | import config from '@/src/globalconfig'; 4 | const { post } = axios; 5 | export async function asyncLogin(data) { 6 | return post('/api/v1/login', qs.stringify(data)); 7 | } 8 | 9 | export async function asyncRegister(params) { 10 | return post('/api/v1/register', qs.stringify(params)); 11 | } 12 | -------------------------------------------------------------------------------- /src/pages/Data/components/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Plots } from './Plots'; 2 | export { default as VarianceExplain } from './VarianceExplain'; 3 | export { default as CorrTable } from './CorrTable'; 4 | export { default as CompMatrixTable } from './CompMatrixTable'; 5 | export { default as DataPanel } from './DataPanel'; 6 | export { default as ClusterDisplay } from './ClusterDisplay'; 7 | -------------------------------------------------------------------------------- /src/components/Charts/Field/index.less: -------------------------------------------------------------------------------- 1 | @import "~antd/lib/style/themes/default.less"; 2 | 3 | .field { 4 | white-space: nowrap; 5 | overflow: hidden; 6 | text-overflow: ellipsis; 7 | margin: 0; 8 | span { 9 | font-size: @font-size-base; 10 | line-height: 22px; 11 | } 12 | span:last-child { 13 | margin-left: 8px; 14 | color: @heading-color; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/pages/Data/reduction.less: -------------------------------------------------------------------------------- 1 | @import '../../styles/themes/default'; 2 | 3 | .base-card { 4 | background: @base-white; 5 | box-shadow: @shadow-1; 6 | border-radius: 4px; 7 | } 8 | 9 | .card { 10 | .base-card; 11 | 12 | min-width: 600px; 13 | max-width: 100%; 14 | margin-bottom: 24px; 15 | } 16 | 17 | .plot { 18 | display: flex; 19 | justify-content: center; 20 | } 21 | -------------------------------------------------------------------------------- /src/models/menu.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | state: { 3 | collapsed: false, 4 | visible: true, 5 | }, 6 | reducers: { 7 | handleCollapsed(state, { payload: collapsed }) { 8 | return { ...state, collapsed }; 9 | }, 10 | handleVisibility(state) { 11 | return { 12 | ...state, 13 | visible: !state.visible, 14 | }; 15 | }, 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /data/txt.txt: -------------------------------------------------------------------------------- 1 | 要花族单并毛养个根律京军,完快行转求极板不指芹。 条么素们七由再解做里音,准展那得月A体何本。 关命你南也色的容但果,农省斗见千义常飞称,战好刷到石到真辰。 2 | 3 | 体布命持家劳出快物义,验看性声容取受,细目H奋抓M包团。 中极车料高除即直也六,想增自形便整务带争率,各命豆明板团道M。 见两西世备专点起口子,进时记理自际亲应但导,周叫M认质思指罐。 物必各起代在员叫合,并来效保济变海,节众否枣明极。 4 | 5 | 改为部况近料低区矿员形,布提要则高装个都根质,各常极建串何活不真。 一为率是近市到育,际二斗地响习,不励壳杏己前。 族更更其格上决查速,权件现该争于。 6 | 7 | 但阶价万内数器置适,情族往们拉界张,养事权决教行现B,芦经照心般后。 存干半六例究火分,实个门用世备无,率P束代指术。 一于从千所制,当辰1。 8 | 9 | -------------------------------------------------------------------------------- /src/services/error.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | const { get } = axios; 4 | export async function query404() { 5 | return get('/api/404'); 6 | } 7 | 8 | export async function query401() { 9 | return get('/api/401'); 10 | } 11 | 12 | export async function query403() { 13 | return get('/api/403'); 14 | } 15 | 16 | export async function query500() { 17 | return get('/api/500'); 18 | } 19 | -------------------------------------------------------------------------------- /src/utils/Authorized.ts: -------------------------------------------------------------------------------- 1 | import { RenderAuthorize } from '@/components'; 2 | import { getAuthority } from '@/utils'; 3 | 4 | let Authorized = RenderAuthorize(getAuthority(undefined)); // eslint-disable-line 5 | 6 | // Reload the rights component 7 | const reloadAuthorized = () => { 8 | Authorized = RenderAuthorize(getAuthority(undefined)); 9 | }; 10 | 11 | export {reloadAuthorized, Authorized}; 12 | -------------------------------------------------------------------------------- /src/utils/Authorized-old.js: -------------------------------------------------------------------------------- 1 | import { Authorized as RenderAuthorized } from '../components'; 2 | import { getAuthority } from './authority'; 3 | 4 | let Authorized = RenderAuthorized(getAuthority()); 5 | // Reload the rights component 6 | const reloadAuthorized = () => { 7 | Authorized = RenderAuthorized(getAuthority()); 8 | }; 9 | 10 | // export { reloadAuthorized }; 11 | // export default Authorized; 12 | -------------------------------------------------------------------------------- /src/components/Charts/index.less: -------------------------------------------------------------------------------- 1 | .miniChart { 2 | position: relative; 3 | width: 100%; 4 | .chartContent { 5 | position: absolute; 6 | bottom: -28px; 7 | width: 100%; 8 | > div { 9 | margin: 0 -5px; 10 | overflow: hidden; 11 | } 12 | } 13 | .chartLoading { 14 | position: absolute; 15 | top: 16px; 16 | left: 50%; 17 | margin-left: -7px; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/pages/Interview/components/RecordList/Editor/utils/isSelectionInList.ts: -------------------------------------------------------------------------------- 1 | import { Value } from 'slate'; 2 | 3 | import Options from '../options'; 4 | import getItemsAtRange from './getItemsAtRange'; 5 | 6 | /** 7 | * True if selection is inside a list (and can be unwrapped) 8 | */ 9 | export default (opts: Options, value: Value): boolean => { 10 | return !getItemsAtRange(opts, value).isEmpty(); 11 | }; 12 | -------------------------------------------------------------------------------- /src/pages/Interview/components/TagGroup/DraggableTag.less: -------------------------------------------------------------------------------- 1 | @import '~styles/themes/default'; 2 | 3 | .dragging { 4 | //opacity: 0.5; 5 | :global { 6 | .ant-tag-checkable:active { 7 | background: @blue-4; 8 | color: @transWhite-3; 9 | } 10 | } 11 | } 12 | 13 | .drop-right { 14 | border-right: 2px dashed @blue-3; 15 | } 16 | .drop-left { 17 | border-left: 2px dashed @blue-3; 18 | } 19 | -------------------------------------------------------------------------------- /src/components/Charts/demo/waterwave.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 8 3 | title: 水波图 4 | --- 5 | 6 | 水波图是一种比例的展示方式,可以更直观的展示关键值的占比。 7 | 8 | ````jsx 9 | import { WaterWave } from 'ant-design-pro/lib/Charts'; 10 | 11 | ReactDOM.render( 12 |
13 | 18 |
19 | , mountNode); 20 | ```` 21 | -------------------------------------------------------------------------------- /src/pages/Data/components/ClusterDisplay/color.ts: -------------------------------------------------------------------------------- 1 | export default [ 2 | 'l(0) 0:#99f5ff 1:#a6a6ff', 3 | 'l(0) 0:#fdacc0 1:#fec1fa', 4 | 'l(0) 0:#a6ffd2 1:#86feeb', 5 | 'l(0) 0:#fe9 1:#ffe0a6', 6 | 'l(0) 0:#a6ffd2 1:#d0f0b3', 7 | '#a6a6ff', 8 | '#fdacc0', 9 | '#fe9', 10 | '#fec1fa', 11 | '#99f5ff', 12 | '#dba6ef', 13 | '#a6ffd2', 14 | '#86feeb', 15 | '#d0f0b3', 16 | '#ffe0a6', 17 | ]; 18 | -------------------------------------------------------------------------------- /src/pages/Data/_layout.less: -------------------------------------------------------------------------------- 1 | @import '~styles/themes/default'; 2 | 3 | .container { 4 | background: @white-2; 5 | min-height: 100%; 6 | display: flex; 7 | height: calc(~'100vh - 44px'); 8 | flex-direction: row; 9 | justify-content: space-between; 10 | flex: none; 11 | } 12 | 13 | .left { 14 | width: 100%; 15 | display: flex; 16 | justify-content: center; 17 | overflow: auto; 18 | margin: 24px; 19 | } 20 | -------------------------------------------------------------------------------- /src/components/Authorized/index.js: -------------------------------------------------------------------------------- 1 | import Authorized from './Authorized'; 2 | import AuthorizedRoute from './AuthorizedRoute'; 3 | import Secured from './Secured'; 4 | import check from './CheckPermissions'; 5 | import renderAuthorize from './renderAuthorize'; 6 | 7 | Authorized.Secured = Secured; 8 | Authorized.AuthorizedRoute = AuthorizedRoute; 9 | Authorized.check = check; 10 | 11 | export default renderAuthorize(Authorized); 12 | -------------------------------------------------------------------------------- /src/pages/Interview/components/RecordList/Editor/options.ts: -------------------------------------------------------------------------------- 1 | import { Record } from 'immutable'; 2 | 3 | export type OptionsFormat = { types?: string[]; typeItem?: string; typeDefault?: string }; 4 | 5 | export default class Options extends Record({ 6 | types: ['ul_list'], 7 | typeItem: 'list_item', 8 | typeDefault: 'paragraph', 9 | }) { 10 | types: string[]; 11 | typeItem: string; 12 | typeDefault: string; 13 | } 14 | -------------------------------------------------------------------------------- /src/pages/Interview/components/RecordList/Editor/utils/index.ts: -------------------------------------------------------------------------------- 1 | export { default as getCurrentItem } from './getCurrentItem'; 2 | export { default as getCurrentList } from './getCurrentList'; 3 | export { default as getItemsAtRange } from './getItemsAtRange'; 4 | export { default as getPreviousItem } from './getPreviousItem'; 5 | export { default as isList } from './isList'; 6 | export { default as isSelectionInList } from './isSelectionInList'; 7 | -------------------------------------------------------------------------------- /src/components/Charts/Bar/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | export interface BarProps { 3 | title: React.ReactNode; 4 | color?: string; 5 | padding?: [number, number, number, number]; 6 | height: number; 7 | data: Array<{ 8 | x: string; 9 | y: number; 10 | }>; 11 | autoLabel?: boolean; 12 | style?: React.CSSProperties; 13 | } 14 | 15 | export default class Bar extends React.Component {} 16 | -------------------------------------------------------------------------------- /src/components/Charts/ChartCard/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | export interface ChartCardProps { 3 | title: React.ReactNode; 4 | action?: React.ReactNode; 5 | total?: React.ReactNode | number; 6 | footer?: React.ReactNode; 7 | contentHeight?: number; 8 | avatar?: React.ReactNode; 9 | style?: React.CSSProperties; 10 | } 11 | 12 | export default class ChartCard extends React.Component {} 13 | -------------------------------------------------------------------------------- /src/styles/core/motion.less: -------------------------------------------------------------------------------- 1 | @import "../mixins/motion"; 2 | @import "./motion/fade"; 3 | @import "./motion/move"; 4 | @import "./motion/other"; 5 | @import "./motion/slide"; 6 | @import "./motion/swing"; 7 | @import "./motion/zoom"; 8 | 9 | // For common/openAnimation 10 | .ant-motion-collapse { 11 | overflow: hidden; 12 | &-active { 13 | transition: height .15s @ease-in-out, opacity .15s @ease-in-out !important; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/components/Charts/Radar/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | export interface RadarProps { 3 | title?: React.ReactNode; 4 | height: number; 5 | padding?: [number, number, number, number]; 6 | hasLegend?: boolean; 7 | data: Array<{ 8 | name: string; 9 | label: string; 10 | value: string; 11 | }>; 12 | style?: React.CSSProperties; 13 | } 14 | 15 | export default class Radar extends React.Component {} 16 | -------------------------------------------------------------------------------- /src/pages/Persona/layout.less: -------------------------------------------------------------------------------- 1 | @import '../../styles/themes/default'; 2 | 3 | .layout { 4 | height: 100vh; 5 | display: flex; 6 | flex-direction: column; 7 | } 8 | .container { 9 | background: @white-2; 10 | display: flex; 11 | flex-direction: row; 12 | justify-content: space-between; 13 | height: calc(~'100% - 44px'); 14 | } 15 | 16 | .base-card { 17 | background: @base-white; 18 | box-shadow: @shadow-1; 19 | border-radius: 4px; 20 | } 21 | -------------------------------------------------------------------------------- /src/utils/router.ts: -------------------------------------------------------------------------------- 1 | import pathToRegexp from 'path-to-regexp'; 2 | import { drop, dropRight, tail } from 'lodash'; 3 | 4 | export const getBaseUrl = (pathname: string): string => { 5 | const re = pathToRegexp('*/:panel'); 6 | return dropRight(drop(re.exec(pathname))).toString(); 7 | }; 8 | export const getLastRouter = (pathname: string): string => { 9 | const re = pathToRegexp('*/:panel'); 10 | return tail(drop(re.exec(pathname))).toString(); 11 | }; 12 | -------------------------------------------------------------------------------- /src/components/Charts/TimelineChart/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | export interface TimelineChartProps { 3 | data: Array<{ 4 | x: string; 5 | y1: string; 6 | y2: string; 7 | }>; 8 | titleMap: { y1: string; y2: string }; 9 | padding?: [number, number, number, number]; 10 | height?: number; 11 | style?: React.CSSProperties; 12 | } 13 | 14 | export default class TimelineChart extends React.Component< 15 | TimelineChartProps, 16 | any 17 | > {} 18 | -------------------------------------------------------------------------------- /src/utils/__test__/utils.test.ts: -------------------------------------------------------------------------------- 1 | import { generateKey, reorder } from '@/utils'; 2 | 3 | jest.mock('shortid'); 4 | 5 | describe('generateKey', () => { 6 | it('should return id', () => { 7 | const id = generateKey(); 8 | expect(typeof id).toEqual('string'); 9 | expect(id).toEqual('testKey'); 10 | }); 11 | }); 12 | 13 | describe('reorder', () => { 14 | const arr = ['313', '2543', '75676']; 15 | expect(reorder(arr, 1, 2)).toEqual(['313', '75676', '2543']); 16 | }); 17 | -------------------------------------------------------------------------------- /mock/projects.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'post /api/v1/projects/starred': [ 3 | { name: '1', description: '3' }, 4 | { name: 'name2', description: 'dasdasfe', star: 1 }, 5 | ], 6 | 'get /api/v1/projects': [ 7 | { name: '1', description: '3' }, 8 | { name: 'name2', description: 'dasdasfe', star: 1 }, 9 | ], 10 | 'post /api/v1/projects/recent': [ 11 | { name: '1', description: '3' }, 12 | { name: 'name2', description: 'dasdasfe', star: 1 }, 13 | ], 14 | }; 15 | -------------------------------------------------------------------------------- /src/assets/photos/females/index.ts: -------------------------------------------------------------------------------- 1 | import photo1 from './1.png'; 2 | import photo2 from './2.png'; 3 | import photo3 from './3.png'; 4 | import photo4 from './4.png'; 5 | import photo5 from './5.png'; 6 | import photo6 from './6.png'; 7 | import photo7 from './7.png'; 8 | import photo8 from './8.png'; 9 | import photo9 from './9.png'; 10 | import photo10 from './10.png'; 11 | 12 | 13 | 14 | export default [photo1, photo2, photo3, photo4, photo5, photo6, photo7, photo8, photo9, photo10]; 15 | -------------------------------------------------------------------------------- /src/common/PageTitle.ts: -------------------------------------------------------------------------------- 1 | const PagePostfix = ' | 灵猫'; 2 | const PageTitleList = { 3 | '/login/login': '登录', 4 | '/login/register': '注册', 5 | '/persona/edit': '用户画像编辑', 6 | '/persona/match': '用户画像匹配', 7 | '/project/view': '我的项目' 8 | }; 9 | export const getPageTitle = (pathname) => { 10 | const PageTitle = PageTitleList[pathname] + PagePostfix; 11 | console.log('PageTitle',PageTitle); 12 | if (PageTitleList[pathname] === undefined) return '灵猫'; 13 | return PageTitle; 14 | }; 15 | -------------------------------------------------------------------------------- /data/keyDimensions.ts: -------------------------------------------------------------------------------- 1 | import { generateKey } from '@/utils'; 2 | 3 | export default [ 4 | { 5 | question: { text: '你的名字是?', key: generateKey() }, 6 | answers: [{ text: 'A.小A', order: 0 }, { text: 'B.小B', order: 1 }], 7 | labelKey: 'name', 8 | labelText: '姓名', 9 | }, 10 | { 11 | question: { text: '你的性别是?', key: generateKey() }, 12 | answers: [{ text: 'B.男', order: 1 }, { text: 'A.女', order: 0 }], 13 | labelKey: 'gender', 14 | labelText: '性别', 15 | }, 16 | ]; 17 | -------------------------------------------------------------------------------- /src/components/Authorized/AuthorizedRoute.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { RouteProps } from 'react-router'; 3 | 4 | type authorityFN = (currentAuthority?: string) => boolean; 5 | 6 | type authority = string | string[] | authorityFN | Promise; 7 | 8 | export interface IAuthorizedRouteProps extends RouteProps { 9 | authority: authority; 10 | } 11 | export { authority }; 12 | 13 | export default class AuthorizedRoute extends React.Component {} 14 | -------------------------------------------------------------------------------- /src/components/Ellipsis/index.less: -------------------------------------------------------------------------------- 1 | .ellipsis { 2 | overflow: hidden; 3 | display: inline-block; 4 | word-break: break-all; 5 | width: 100%; 6 | } 7 | 8 | .lines { 9 | position: relative; 10 | .shadow { 11 | display: block; 12 | position: relative; 13 | color: transparent; 14 | opacity: 0; 15 | z-index: -999; 16 | } 17 | } 18 | 19 | .lineClamp { 20 | position: relative; 21 | overflow: hidden; 22 | text-overflow: ellipsis; 23 | display: -webkit-box; 24 | } 25 | -------------------------------------------------------------------------------- /src/components/Login/LoginSubmit.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import classNames from 'classnames'; 3 | import { Button, Form } from 'antd'; 4 | import styles from './index.less'; 5 | 6 | const FormItem = Form.Item; 7 | 8 | export default ({ className, ...rest }) => { 9 | const clsString = classNames(styles.submit, className); 10 | return ( 11 | 12 |