diff --git a/package.json b/package.json index 979081a..15708e5 100644 --- a/package.json +++ b/package.json @@ -23,11 +23,14 @@ "@ant-design/charts": "^1.4.2", "@ant-design/pro-layout": "6.32.1", "@formily/antd": "^2.0.6", + "@jiaminghi/data-view-react": "^1.2.5", "@types/lodash": "^4.14.172", "@ztree/ztree_v3": "^3.5.42", "ahooks": "^3.1.3", "antd": "^4.17.3", "axios": "^0.22.0", + "echarts": "^4.9.0", + "echarts-gl": "^1.1.2", "fbemitter": "^3.0.0", "js-base64": "^3.6.1", "js-cookie": "^2.2.1", @@ -58,7 +61,6 @@ "@types/react": "^17.0.2", "@types/react-dom": "^17.0.2", "@umijs/plugin-access": "2.4.2", - "umi-plugin-authorize": "^2.8.12", "@umijs/plugin-dva": "^0.13.0", "@umijs/plugin-initial-state": "^2.4.0", "@umijs/plugin-locale": "^0.15.0", @@ -71,6 +73,7 @@ "prettier": "^2.2.0", "typescript": "^4.3.5", "umi": "^3.5.20", + "umi-plugin-authorize": "^2.8.12", "yorkie": "^2.0.0" } } diff --git a/src/assets/images/card_bg.png b/src/assets/images/card_bg.png new file mode 100644 index 0000000..e27a78e Binary files /dev/null and b/src/assets/images/card_bg.png differ diff --git a/src/assets/images/frame_bg.png b/src/assets/images/frame_bg.png new file mode 100644 index 0000000..9578b5b Binary files /dev/null and b/src/assets/images/frame_bg.png differ diff --git a/src/assets/images/frame_icon.png b/src/assets/images/frame_icon.png new file mode 100644 index 0000000..3589658 Binary files /dev/null and b/src/assets/images/frame_icon.png differ diff --git a/src/assets/images/frame_title_bg.png b/src/assets/images/frame_title_bg.png new file mode 100644 index 0000000..84aa16b Binary files /dev/null and b/src/assets/images/frame_title_bg.png differ diff --git a/src/assets/images/head_bg1.png b/src/assets/images/head_bg1.png new file mode 100644 index 0000000..ef18d95 Binary files /dev/null and b/src/assets/images/head_bg1.png differ diff --git a/src/assets/images/head_title.png b/src/assets/images/head_title.png new file mode 100644 index 0000000..d05b0b1 Binary files /dev/null and b/src/assets/images/head_title.png differ diff --git a/src/assets/images/hxzgzgdjfx.png b/src/assets/images/hxzgzgdjfx.png new file mode 100644 index 0000000..16052ce Binary files /dev/null and b/src/assets/images/hxzgzgdjfx.png differ diff --git a/src/assets/images/hxzgzgnljg.png b/src/assets/images/hxzgzgnljg.png new file mode 100644 index 0000000..62575a6 Binary files /dev/null and b/src/assets/images/hxzgzgnljg.png differ diff --git a/src/assets/images/hxzgzgrybdtj.png b/src/assets/images/hxzgzgrybdtj.png new file mode 100644 index 0000000..46695c1 Binary files /dev/null and b/src/assets/images/hxzgzgrybdtj.png differ diff --git a/src/assets/images/hxzgzgxljg.png b/src/assets/images/hxzgzgxljg.png new file mode 100644 index 0000000..51c0432 Binary files /dev/null and b/src/assets/images/hxzgzgxljg.png differ diff --git a/src/assets/images/more.png b/src/assets/images/more.png new file mode 100644 index 0000000..d536f17 Binary files /dev/null and b/src/assets/images/more.png differ diff --git a/src/assets/images/one.png b/src/assets/images/one.png new file mode 100644 index 0000000..0a3c5d3 Binary files /dev/null and b/src/assets/images/one.png differ diff --git a/src/assets/images/pageBg.png b/src/assets/images/pageBg.png new file mode 100644 index 0000000..8e03594 Binary files /dev/null and b/src/assets/images/pageBg.png differ diff --git a/src/assets/images/refresh.png b/src/assets/images/refresh.png new file mode 100644 index 0000000..9b6fbbd Binary files /dev/null and b/src/assets/images/refresh.png differ diff --git a/src/assets/images/rwdcl.png b/src/assets/images/rwdcl.png new file mode 100644 index 0000000..1d0179c Binary files /dev/null and b/src/assets/images/rwdcl.png differ diff --git a/src/assets/images/rylbylb.png b/src/assets/images/rylbylb.png new file mode 100644 index 0000000..b72a7cf Binary files /dev/null and b/src/assets/images/rylbylb.png differ diff --git a/src/assets/images/tabSelected.png b/src/assets/images/tabSelected.png new file mode 100644 index 0000000..a4206a6 Binary files /dev/null and b/src/assets/images/tabSelected.png differ diff --git a/src/assets/images/three.png b/src/assets/images/three.png new file mode 100644 index 0000000..3df3b29 Binary files /dev/null and b/src/assets/images/three.png differ diff --git a/src/assets/images/two.png b/src/assets/images/two.png new file mode 100644 index 0000000..b3735fe Binary files /dev/null and b/src/assets/images/two.png differ diff --git a/src/assets/images/weaverlogo.png b/src/assets/images/weaverlogo.png new file mode 100644 index 0000000..42106a4 Binary files /dev/null and b/src/assets/images/weaverlogo.png differ diff --git a/src/assets/images/xmys.png b/src/assets/images/xmys.png new file mode 100644 index 0000000..d1bc40a Binary files /dev/null and b/src/assets/images/xmys.png differ diff --git a/src/assets/images/xsje.png b/src/assets/images/xsje.png new file mode 100644 index 0000000..f3fe62a Binary files /dev/null and b/src/assets/images/xsje.png differ diff --git a/src/assets/images/yjgz.png b/src/assets/images/yjgz.png new file mode 100644 index 0000000..aee88ce Binary files /dev/null and b/src/assets/images/yjgz.png differ diff --git a/src/assets/images/zgxcfftj.png b/src/assets/images/zgxcfftj.png new file mode 100644 index 0000000..07744ca Binary files /dev/null and b/src/assets/images/zgxcfftj.png differ diff --git a/src/assets/images/zs.png b/src/assets/images/zs.png new file mode 100644 index 0000000..7d60f57 Binary files /dev/null and b/src/assets/images/zs.png differ diff --git a/src/layouts/index.tsx b/src/layouts/index.tsx index 9369776..6243e07 100644 --- a/src/layouts/index.tsx +++ b/src/layouts/index.tsx @@ -15,6 +15,7 @@ import { layoutConfig } from "@/layouts/config"; import stores from "@/store"; import "moment/locale/zh-cn"; import "antd/dist/antd.variable.min.css"; +import "../utils/flexible"; moment.locale("zh-cn"); diff --git a/src/pages/personnelReport/constants.js b/src/pages/personnelReport/constants.js index 3c52dfa..0885a7b 100644 --- a/src/pages/personnelReport/constants.js +++ b/src/pages/personnelReport/constants.js @@ -2,6 +2,58 @@ import { G2, measureTextWidth } from "@ant-design/plots"; const G = G2.getEngine("canvas"); +export const structureCardList = [ + { + title: "当期总人数", + active: 0, + number: 489, + unit: "人", + color: "#FFBB31", + tabList: ["本月", "上月"] + }, + { + title: "本月入职", + active: 0, + number: 100, + unit: "人", + color: "#3DA5F6", + tabList: ["本月", "上月"] + }, + { + title: "本月离职", + active: 0, + number: 23, + unit: "人", + color: "#5FD5C7", + tabList: ["本月", "上月"] + }, + { + title: "年度累计入职", + active: 0, + number: 238, + unit: "人", + color: "#2B6DF6", + tabList: ["本年", "去年"] + }, + { + title: "年度累计离职", + active: 0, + number: 198, + unit: "人", + color: "#B571EA", + tabList: ["本年", "去年"] + }, + { + title: "平均年龄", + active: 0, + number: 28.0, + unit: "岁", + color: "#5FCC7B", + tabList: ["本年", "去年"] + } +]; + + //人员流动数据 export const flowingData = [ { @@ -44,14 +96,10 @@ export const flowingConfig = { offset: "-2%", content: "{name}", style: { - fontSize: 10 + fontSize: 15 } }, - interactions: [ - { - type: "element-active" - } - ], + interactions: [], pieStyle: { lineWidth: 0 } @@ -179,11 +227,7 @@ export const seniorityConfig = { textAlign: "center" } }, - interactions: [ - { - type: "element-active" - } - ] + interactions: [] }; //人员流动情况趋势图数据信息 @@ -279,8 +323,8 @@ export const institutionConfig = { data: institutionData, xField: "stage", yField: "number", - dynamicHeight: true, - legend: false + dynamicHeight: true + // legend: false }; //人员异动分析数数据 @@ -300,7 +344,7 @@ export const personChangeData = [ ]; export const personChangeConfig = { appendPadding: 10, - data:[], + data: [], angleField: "value", colorField: "type", radius: 0.75, @@ -309,104 +353,46 @@ export const personChangeConfig = { labelHeight: 28, content: "{name}\n{percentage}" }, - interactions: [ - { - type: "element-active" - } - ] + interactions: [] }; //详细图表柱状图 export const multiplData = [ - { - "name": "全员离职率", - "年份": "2022", - "年份流动信息": 18.9 - }, - { - "name": "全员离职率", - "年份": "2023", - "年份流动信息": 28.8 - }, - { - "name": "主动离职率", - "年份": "2022", - "年份流动信息": 12.4 - }, - { - "name": "主动离职率", - "年份": "2023", - "年份流动信息": 23.2 - }, - { - "name": "被动离职率", - "年份": "2022", - "年份流动信息": 12.4 - }, - { - "name": "被动离职率", - "年份": "2023", - "年份流动信息": 23.2 - }, - { - "name": "关键岗位离职率", - "年份": "2022", - "年份流动信息": 12.4 - }, - { - "name": "关键岗位离职率", - "年份": "2023", - "年份流动信息": 23.2 - }, - { - "name": "绩优员工离职率", - "年份": "2022", - "年份流动信息": 12.4 - }, - { - "name": "绩优员工离职率", - "年份": "2023", - "年份流动信息": 23.2 - }, - { - "name": "转正率", - "年份": "2022", - "年份流动信息": 12.4 - }, - { - "name": "转正率", - "年份": "2023", - "年份流动信息": 23.2 - }, + { "city": "12", "type": "今年", "value": 18000 }, + { "city": "12", "type": "去年", "value": 11000 }, + { "city": "11", "type": "今年", "value": 18000 }, + { "city": "11", "type": "去年", "value": 11000 }, + { "city": "10", "type": "今年", "value": 18000 }, + { "city": "10", "type": "去年", "value": 11000 }, + { "city": "09", "type": "今年", "value": 18000 }, + { "city": "09", "type": "去年", "value": 11000 }, + { "city": "08", "type": "今年", "value": 18000 }, + { "city": "08", "type": "去年", "value": 11000 }, + { "city": "07", "type": "今年", "value": 17000 }, + { "city": "07", "type": "去年", "value": 6000 }, + { "city": "06", "type": "今年", "value": 9000 }, + { "city": "06", "type": "去年", "value": 8500 }, + { "city": "05", "type": "今年", "value": 14000 }, + { "city": "05", "type": "去年", "value": 9000 }, + { "city": "04", "type": "今年", "value": 14000 }, + { "city": "04", "type": "去年", "value": 9000 }, + { "city": "03", "type": "今年", "value": 16000 }, + { "city": "03", "type": "去年", "value": 5000 }, + { "city": "02", "type": "今年", "value": 9000 }, + { "city": "02", "type": "去年", "value": 8500 }, + { "city": "01", "type": "今年", "value": 14500 }, + { "city": "01", "type": "去年", "value": 8500 } ]; //人员流动情况趋势图配置信息 export const multipleConfig = { + data: [], + xField: "city", + yField: "value", + seriesField: "type", isGroup: true, - xField: "年份", - yField: "年份流动信息", - seriesField: "name", - // 分组柱状图 组内柱子间的间距 (像素级别) - dodgePadding: 2, - // 分组柱状图 组间的间距 (像素级别) - intervalPadding: 50, - label: { - // 可手动配置 label 数据标签位置 - position: "middle", - // 'top', 'middle', 'bottom' - // 可配置附加的布局方法 - layout: [ - // 柱形图数据标签位置自动调整 - { - type: "interval-adjust-position" - }, // 数据标签防遮挡 - { - type: "interval-hide-overlap" - }, // 数据标签文颜色自动调整 - { - type: "adjust-color" - } - ] + columnStyle: { + radius: [20, 20, 0, 0] } }; @@ -423,104 +409,69 @@ export const trainingData = [ ]; //培训排名 -export const rankingData =[ - { - name: '机构一', - month: 'Jan.', - grades: 18.9, - }, - { - name: '机构一', - month: 'Feb.', - grades: 28.8, - }, - { - name: '机构一', - month: 'Mar.', - grades: 39.3, - }, +export const rankingData = [ { - name: '机构一', - month: 'Apr.', - grades: 81.4, + label: "财务部", + type: "前五", + value: 2800 }, { - name: '机构一', - month: 'May', - grades: 47, + label: "销售部", + type: "前五", + value: 1800 }, { - name: '机构一', - month: 'Jun.', - grades: 20.3, + label: "策划部", + type: "前五", + value: 950 }, { - name: '机构一', - month: 'Jul.', - grades: 24, + label: "运营部", + type: "前五", + value: 500 }, { - name: '机构一', - month: 'Aug.', - grades: 35.6, + label: "后勤部", + type: "前五", + value: 170 }, { - name: '机构二', - month: 'Jan.', - grades: 12.4, + label: "财务部1", + type: "后五", + value: 2260 }, { - name: '机构二', - month: 'Feb.', - grades: 23.2, + label: "销售部1", + type: "后五", + value: 1300 }, { - name: '机构二', - month: 'Mar.', - grades: 34.5, + label: "策划部1", + type: "后五", + value: 900 }, { - name: '机构二', - month: 'Apr.', - grades: 99.7, + label: "运营部1", + type: "后五", + value: 390 }, { - name: '机构二', - month: 'May', - grades: 52.6, - }, - { - name: '机构二', - month: 'Jun.', - grades: 35.5, - }, - { - name: '机构二', - month: 'Jul.', - grades: 37.4, - }, - { - name: '机构二', - month: 'Aug.', - grades: 42.4, - }, -] + label: "后勤部1", + type: "后五", + value: 100 + } +]; export const rankingConfig = { + data: [], isGroup: true, - xField: "month", - yField: "grades", - seriesField: "name", - // 分组柱状图 组内柱子间的间距 (像素级别) - dodgePadding: 2, - // 分组柱状图 组间的间距 (像素级别) - intervalPadding: 10, - columnStyle: { - radius: [20, 20, 0], - }, + xField: "value", + yField: "label", + seriesField: "type", + dodgePadding: 4, label: { // 可手动配置 label 数据标签位置 position: "middle", - // 'top', 'middle', 'bottom' + // 'left', 'middle', 'right' // 可配置附加的布局方法 layout: [ // 柱形图数据标签位置自动调整 @@ -602,257 +553,249 @@ export const humanAnalysisconfig = { //人效排名数据 export const humanEfficiencyRankingData = [ { - title: '员工1', + title: "员工1", description: "公司1", - rank:1 + rank: 1 }, { - title: '员工2', + title: "员工2", description: "公司2", - rank:2 + rank: 2 }, { - title: '员工3', + title: "员工3", description: "公司3", - rank:3 + rank: 3 }, { - title: '员工4', + title: "员工4", description: "公司4", - rank:4 - }, + rank: 4 + } ]; //销售新客户合同数量排名 export const saleEfficiencyRankingData = [ { - title: '员工5', + title: "员工5", description: "公司1", - rank:1 + rank: 1 }, { - title: '员工6', + title: "员工6", description: "公司2", - rank:2 + rank: 2 }, { - title: '员工7', + title: "员工7", description: "公司3", - rank:3 + rank: 3 }, { - title: '员工8', + title: "员工8", description: "公司4", - rank:4 - }, + rank: 4 + } ]; //新销售有效合同 export const newSaleEfficiencyRankingData = [ { - title: '员工9', + title: "员工9", description: "公司1", - rank:1 + rank: 1 }, { - title: '员工10', + title: "员工10", description: "公司2", - rank:2 + rank: 2 }, { - title: '员工11', + title: "员工11", description: "公司3", - rank:3 + rank: 3 }, { - title: '员工12', + title: "员工12", description: "公司4", - rank:4 - }, + rank: 4 + } ]; - //人工总成本数据 export const totalLaborCostdata = [ { - time: 'Jan.', + time: "Jan.", value: 350, - count: 800, + count: 800 }, { - time: 'Feb.', + time: "Feb.", value: 900, - count: 600, + count: 600 }, { - time: 'Mar.', + time: "Mar.", value: 300, - count: 400, + count: 400 }, { - time: 'Apr.', + time: "Apr.", value: 450, - count: 380, + count: 380 }, { - time: 'May.', + time: "May.", value: 470, - count: 220, + count: 220 }, { - time: 'Jun.', + time: "Jun.", value: 470, - count: 220, + count: 220 }, { - time: 'Jul.', + time: "Jul.", value: 470, - count: 220, + count: 220 }, { - time: 'Aug.', + time: "Aug.", value: 470, - count: 220, - }, + count: 220 + } ]; export const totalLaborCostConfig = { data: [totalLaborCostdata, totalLaborCostdata], - xField: 'time', - yField: ['value', 'count'], + xField: "time", + yField: ["value", "count"], yAxis: { // 格式化左坐标轴 value: { min: 0, label: { - formatter: (val) => `${val}W`, - }, + formatter: (val) => `${val}W` + } }, // 隐藏右坐标轴 - count: false, + count: false }, geometryOptions: [ { - geometry: 'column', - color: '#5B8FF9', + geometry: "column", + color: "#5B8FF9", columnWidthRatio: 0.4, label: { - position: 'middle', - }, + position: "middle" + } }, { - geometry: 'line', + geometry: "line", smooth: true, - color: '#5AD8A6', - }, - ], - interactions: [ - { - type: 'element-highlight', - }, - { - type: 'active-region', - }, + color: "#5AD8A6" + } ], + interactions: [], annotations: { value: [ { - type: 'text', - position: ['2019-06', 'max'], - content: '柱线混合图', - }, + type: "text", + position: ["2019-06", "max"], + content: "柱线混合图" + } ], count: [ { - type: 'dataMarker', + type: "dataMarker", top: true, - position: ['2019-05', 400], + position: ["2019-05", 400], line: { - length: 20, + length: 20 }, text: { - content: '2019-05, 发布新版本', + content: "", style: { - textAlign: 'left', - }, - }, - }, - ], - }, + textAlign: "left" + } + } + } + ] + } }; //人均收入配置数据 1、人均固定收入 2、人均浮动收入 export const fixedIncomePerData = [ { - type: '销售', - value: 27, + type: "销售", + value: 27 }, { - type: '项目', - value: 25, + type: "项目", + value: 25 }, { - type: '开发', - value: 18, + type: "开发", + value: 18 }, { - type: '客服', - value: 15, + type: "客服", + value: 15 }, { - type: '其他', - value: 10, + type: "其他", + value: 10 }, { - type: '其他', - value: 5, - }, + type: "其他", + value: 5 + } ]; export const floatingIncomePerData = [ { - type: '销售', - value: 18, + type: "销售", + value: 18 }, { - type: '项目', - value: 29, + type: "项目", + value: 29 }, { - type: '开发', - value: 23, + type: "开发", + value: 23 }, { - type: '客服', - value: 3, + type: "客服", + value: 3 }, { - type: '其他', - value: 22, + type: "其他", + value: 22 }, { - type: '其他', - value: 5, - }, + type: "其他", + value: 5 + } ]; //人均收入配置信息 export const perCapitaIncomeConfig = { appendPadding: 10, - data:[], - angleField: 'value', - colorField: 'type', + data: [], + angleField: "value", + colorField: "type", radius: 1, innerRadius: 0.64, meta: { value: { - formatter: (v) => `${v} ¥`, - }, + formatter: (v) => `${v} ¥` + } }, label: { - type: 'inner', - offset: '-50%', + type: "inner", + offset: "-50%", style: { - textAlign: 'center', + textAlign: "center" }, autoRotate: false, - content: '{value}', + content: "{value}" }, statistic: { title: { @@ -861,119 +804,117 @@ export const perCapitaIncomeConfig = { const { width, height } = container.getBoundingClientRect(); const d = Math.sqrt(Math.pow(width / 2, 2) + Math.pow(height / 2, 2)); //总计 - const text = datum ? datum.type : ''; + const text = datum ? datum.type : ""; return renderStatistic(d, text, { - fontSize: 28, + fontSize: 28 }); - }, + } }, content: { offsetY: 4, style: { - fontSize: '32px', + fontSize: "32px" }, customHtml: (container, view, datum, data) => { const { width } = container.getBoundingClientRect(); const text = datum ? `¥ ${datum.value}` : `¥ ${data.reduce((r, d) => r + d.value, 0)}`; return renderStatistic(width, "", { - fontSize: 32, + fontSize: 32 }); - }, - }, + } + } }, // 添加 中心统计文本 交互 - interactions: [ - ], + interactions: [] }; - //人均人工成本数据 export const laborCostsPerCapitaData = [ { - year: 'Jan.', + year: "Jan.", value: 3, - count: 10, + count: 10 }, { - year: 'Feb.', + year: "Feb.", value: 4, - count: 4, + count: 4 }, { - year: 'Mar.', + year: "Mar.", value: 3.5, - count: 5, + count: 5 }, { - year: 'Apr.', + year: "Apr.", value: 5, - count: 5, + count: 5 }, { - year: 'May.', + year: "May.", value: 4.9, - count: 4.9, + count: 4.9 }, { - year: 'Jun.', + year: "Jun.", value: 6, - count: 35, + count: 35 }, { - year: 'Jul.', + year: "Jul.", value: 7, - count: 7, + count: 7 }, { - year: 'Aug.', + year: "Aug.", value: 9, - count: 1, - }, + count: 1 + } ]; export const laborCostsPerCapitaConfig = { data: [laborCostsPerCapitaData, laborCostsPerCapitaData], - xField: 'year', - yField: ['value', 'count'], + xField: "year", + yField: ["value", "count"], geometryOptions: [ { - geometry: 'line', + geometry: "line", smooth: true, - color: '#5B8FF9', + color: "#5B8FF9", label: { formatter: (datum) => { return `${datum.value}w`; - }, + } }, lineStyle: { lineWidth: 3, - lineDash: [5, 5], - }, + lineDash: [5, 5] + } }, { - geometry: 'line', + geometry: "line", smooth: true, - color: '#5AD8A6', + color: "#5AD8A6", lineStyle: { lineWidth: 4, - opacity: 0.5, + opacity: 0.5 }, label: { formatter: (datum) => { return `${datum.count}w`; - }, + } }, point: { - shape: 'circle', + shape: "circle", size: 4, style: { opacity: 0.5, - stroke: '#5AD8A6', - fill: '#fff', - }, - }, - }, - ], + stroke: "#5AD8A6", + fill: "#fff" + } + } + } + ] }; function renderStatistic(containerWidth, text, style) { @@ -987,7 +928,7 @@ function renderStatistic(containerWidth, text, style) { } const textStyleStr = `width:${containerWidth}px;`; - return `
${text}
`; + return `
${text}
`; } diff --git a/src/pages/personnelReport/index.less b/src/pages/personnelReport/index.less deleted file mode 100644 index ec9a462..0000000 --- a/src/pages/personnelReport/index.less +++ /dev/null @@ -1,64 +0,0 @@ -.chartWrapper { - display: flex; - flex-direction: column; - width: 100%; - height: 100%; - background: #e5e5e5; - padding: 8px; - overflow: hidden; - - .talentTableBox, .flowRateTableBox, .profitTableBox { - display: flex; - align-items: center; - - & > div:not(:last-child) { - margin-right: 8px; - } - - & > div { - flex: 1; - height: 100%; - position: relative; - background: #fff; - padding: 40px 10px 8px; - overflow: hidden; - - :global { - .ant-tabs { - height: 100%; - line-height: normal; - - .ant-tabs-content-holder { - flex: 1; - - .ant-tabs-content { - height: 100%; - - .ant-tabs-tabpane { - overflow-y: auto; - - & > div { - height: 100% !important; - } - } - } - } - } - } - - .title { - position: absolute; - left: 2%; - top: 2%; - font-size: 16px; - font-weight: 700; - } - } - } - - & > div { - margin-bottom: 8px; - flex: 1; - overflow: hidden; - } -} diff --git a/src/pages/personnelReport/peopleFlowTable/index.less b/src/pages/personnelReport/peopleFlowTable/index.less new file mode 100644 index 0000000..4ac2fa7 --- /dev/null +++ b/src/pages/personnelReport/peopleFlowTable/index.less @@ -0,0 +1,6 @@ +.peopleFlowWrapper { + width: 100%; + height: 100%; + background: #e5e5e5; + overflow: auto; +} diff --git a/src/pages/personnelReport/peopleFlowTable/index.tsx b/src/pages/personnelReport/peopleFlowTable/index.tsx new file mode 100644 index 0000000..6fdbba3 --- /dev/null +++ b/src/pages/personnelReport/peopleFlowTable/index.tsx @@ -0,0 +1,23 @@ +/* + * Author: 黎永顺 + * name: 人员流量表 + * Description: + * Date: 2023/4/27 + */ +import React, { FunctionComponent } from "react"; +import styles from "./index.less"; + +interface OwnProps { +} + +type Props = OwnProps; + +const index: FunctionComponent = (props) => { + + return ( +
+
+ ); +}; + +export default index; diff --git a/src/pages/personnelReport/reportScreen/components/echarts/ageStructureBar.tsx b/src/pages/personnelReport/reportScreen/components/echarts/ageStructureBar.tsx new file mode 100644 index 0000000..439e612 --- /dev/null +++ b/src/pages/personnelReport/reportScreen/components/echarts/ageStructureBar.tsx @@ -0,0 +1,116 @@ +const AgeStructureBar = (echarts: any) => { + // 实例化对象 + const myChart = echarts.init(document.getElementById("ageStructure")); + // 配置项 + const option = { + legend: { + icon: "circle", + top: "-1%", + right: "0%", + itemGap: 20, + textStyle:{ + fontSize: 12,//字体大小 + color: '#ffffff'//字体颜色 + }, + }, + tooltip: { + // 坐标轴指示器,坐标轴触发有效 + trigger: "axis", + axisPointer: { + // 默认为直线,可选为:'line' | 'shadow' + type: "shadow" + } + }, + grid: { + left: "0%", + top: 20, + right: "0%", + bottom: "0%", + containLabel: true + }, + xAxis: { + type: "value", + show: false, + axisLine: { + show: false + }, + splitLine: { + show: false + }, + axisTick: { + show: false // 不显示坐标轴刻度线 + } + }, + yAxis: { + type: "category", + data: ["30岁以下", "30~40岁", "40~50岁", "50~60岁", "60岁以上"], + // 修改坐标值样式 + axisLine: { + show: false + }, + axisLabel: { + color: "rgba(255, 255, 255, 1)", + fontSize: 10, + show: true + }, + axisTick: { + show: false // 不显示坐标轴刻度线 + } + }, + series: [ + { + name: "男", + type: "bar", + stack: "total", + barWidth: "30%", + label: { + show: true, + color: "rgba(255, 255, 255, 1)", + fontSize: 10 + }, + emphasis: { + focus: "series" + }, + data: [320, 302, 301, 334, 390], + // bar 样式修改 + itemStyle: { + barBorderRadius: [10, 0 , 0 , 10], + color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [ + { offset: 0, color: "#3EA1FF" }, + { offset: 0.5, color: "#72CFFF" }, + { offset: 1, color: "#72CFFF" } + ]), + }, + }, + { + name: "女", + type: "bar", + stack: "total", + barWidth: "30%", + label: { + show: true, + color: "rgba(255, 255, 255, 1)", + fontSize: 10 + }, + emphasis: { + focus: "series" + }, + data: [120, 132, 101, 134, 90], + // bar 样式修改 + itemStyle: { + barBorderRadius: [0, 10 , 10 , 0], + color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [ + { offset: 0, color: "#B2FDB2" }, + { offset: 0.5, color: "#00D5FF" }, + { offset: 1, color: "#00D5FF" } + ]), + }, + } + ] + }; + // 配置项给实例对象 + myChart.setOption(option); + return myChart; +}; + +export default AgeStructureBar; diff --git a/src/pages/personnelReport/reportScreen/components/echarts/changeStatisticsBar.tsx b/src/pages/personnelReport/reportScreen/components/echarts/changeStatisticsBar.tsx new file mode 100644 index 0000000..55d1bea --- /dev/null +++ b/src/pages/personnelReport/reportScreen/components/echarts/changeStatisticsBar.tsx @@ -0,0 +1,119 @@ +const PersonnelCategoryBar = (echarts: any) => { + // 实例化对象 + const myChart = echarts.init(document.getElementById("changeStatistics")); + // 配置项 + const option = { + tooltip: { + // 坐标轴指示器,坐标轴触发有效 + trigger: "axis", + axisPointer: { + // 默认为直线,可选为:'line' | 'shadow' + type: "shadow" + } + }, + grid: { + left: "0%", + top: 10, + right: "0%", + bottom: "4%", + // true: 数值离DOM盒子的距离 false: 坐标轴离DOM盒子的距离 + containLabel: true + }, + xAxis: [ + { + type: "category", + data: ["公司本部", "公司1", "公司2", "公司3", "公司4", "公司5", "公司6", "公司7"], + axisTick: { + alignWithLabel: true + }, + // 修改坐标值样式 + axisLabel: { + color: "rgba(255, 255, 255, 1)", + fontSize: 10, + show: true + }, + axisLine: { + show: false + } + } + ], + yAxis: [ + { + type: "value", + // 修改坐标值样式 + axisLabel: { + color: "rgba(255, 255, 255, 1)", + fontSize: 12 + }, + // 修改坐标轴线样式 + axisLine: { + show: false + }, + axisTick: { + show: false // 不显示坐标轴刻度线 + }, + splitLine: { + lineStyle: { + type: "dashed", + color: "rgba(93,126,158,1)" + } + } + } + ], + series: [ + { + name: "入职", + type: "bar", + showBackground: false, + barWidth: "10%", + data: [250, 250, 320, 410, 280, 320, 405, 405], + // bar 样式修改 + itemStyle: { + barBorderRadius: 10, + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { offset: 0, color: "#3EA1FF" }, + { offset: 0.5, color: "#72CFFF" }, + { offset: 1, color: "#72CFFF" } + ]) + } + }, + { + name: "离职", + type: "bar", + showBackground: false, + barWidth: "10%", + data: [370, 370, 260, 110, 390, 280, 110, 110], + // bar 样式修改 + itemStyle: { + barBorderRadius: 10, + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { offset: 0, color: "#00D5FF" }, + { offset: 0.5, color: "#B2FDB2" }, + { offset: 1, color: "#B2FDB2" } + ]) + } + }, + { + name: "调职", + type: "bar", + showBackground: false, + barWidth: "10%", + data: [310, 310, 80, 280, 310, 70, 280, 280], + // bar 样式修改 + itemStyle: { + barBorderRadius: 10, + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { offset: 0, color: "#1455FF" }, + { offset: 0.5, color: "#5F9CFF" }, + { offset: 1, color: "#5F9CFF" } + ]) + } + } + ] + }; + // 配置项给实例对象 + myChart.setOption(option); + return myChart; +}; + +export default PersonnelCategoryBar; diff --git a/src/pages/personnelReport/reportScreen/components/echarts/degreeStructurePie.tsx b/src/pages/personnelReport/reportScreen/components/echarts/degreeStructurePie.tsx new file mode 100644 index 0000000..acec05d --- /dev/null +++ b/src/pages/personnelReport/reportScreen/components/echarts/degreeStructurePie.tsx @@ -0,0 +1,103 @@ +import { getPie3D } from "@/utils/chart"; + +const color = ["#FE772D", "#FEDB4B", "#2A71FF", "#00EDFE"]; + +const DegreeStructurePie = (echarts: any) => { + // 实例化对象 + const myChart = echarts.init(document.getElementById("degreeStructure")); + // 配置项 + const option = { + ...getPie3D(setLabel([ + { + name: "研究生(博士)", + value: 176 + }, + { + name: "研究生(硕士)", + value: 288 + }, + { + name: "本科", + value: 588 + }, + { + name: "大专及以下", + value: 78 + } + ], color), 0.6, 240, 26, 18, 1, true) + }; + // 配置项给实例对象 + myChart.setOption({ + ...option, + legend: { + orient: "vertical", + icon: "circle", + top: "25%", + left: "10%", + itemGap: 28, + textStyle: { + fontSize: 12,//字体大小 + color: "#ffffff"//字体颜色 + } + } + }); + return myChart; +}; + +export default DegreeStructurePie; + +export const setLabel = (optionData: any, color: any) => { + return optionData.map((item: any, index: number) => { + return { + ...item, + itemStyle: { + color: color[index] + }, + label: { + normal: { + show: false, + color: color[index], + position: "right", + // distance:-10, + offset: [0, 3], + formatter: [ + "{d|{d}%}", + "————", + // '{c|{c}}{b|台}', + "{b|{b}}" + ].join("\n"), // 用\n来换行 + rich: { + b: { + // color: '#fff', + lineHeight: 25, + align: "left", + color: color[index] + }, + c: { + fontSize: 22, + textShadowColor: "#1c90a6", + textShadowOffsetX: 0, + textShadowOffsetY: 2, + textShadowBlur: 5, + color: color[index] + }, + d: { + color: color[index], + align: "left" + } + } + } + }, + labelLine: { + normal: { + show: false, + length2: 30, + lineStyle: { + width: 1, + color: color[index] + } + } + } + }; + }); +}; diff --git a/src/pages/personnelReport/reportScreen/components/echarts/gradAnalysisPie.tsx b/src/pages/personnelReport/reportScreen/components/echarts/gradAnalysisPie.tsx new file mode 100644 index 0000000..f1c0ce1 --- /dev/null +++ b/src/pages/personnelReport/reportScreen/components/echarts/gradAnalysisPie.tsx @@ -0,0 +1,47 @@ +import { getPie3D } from "@/utils/chart"; +import { setLabel } from "./degreeStructurePie"; + +const color = ["#BD42EF", "#6D45F3", "#3358F3", "#6ECDE5"]; +const GradAnalysisPie = (echarts: any) => { + // 实例化对象 + const myChart = echarts.init(document.getElementById("gradAnalysis")); + // 配置项 + const option = { + ...getPie3D(setLabel([ + { + name: "正高级", + value: 500 + }, + { + name: "副高级", + value: 1120 + }, + { + name: "中级", + value: 4567 + }, + { + name: "初级", + value: 5278 + } + ], color), 0.6, 240, 26, 18, 1, false) + }; + // 配置项给实例对象 + myChart.setOption({ + ...option, + legend: { + orient: "vertical", + icon: "circle", + top: "25%", + left: "10%", + itemGap: 28, + textStyle: { + fontSize: 12,//字体大小 + color: "#ffffff"//字体颜色 + } + } + }); + return myChart; +}; + +export default GradAnalysisPie; diff --git a/src/pages/personnelReport/reportScreen/components/echarts/personnelCategoryBar.tsx b/src/pages/personnelReport/reportScreen/components/echarts/personnelCategoryBar.tsx new file mode 100644 index 0000000..caca20a --- /dev/null +++ b/src/pages/personnelReport/reportScreen/components/echarts/personnelCategoryBar.tsx @@ -0,0 +1,96 @@ +const PersonnelCategoryBar = (echarts: any) => { + // 实例化对象 + const myChart = echarts.init(document.getElementById("personnelCategory")); + // 配置项 + const option = { + tooltip: { + // 坐标轴指示器,坐标轴触发有效 + trigger: "axis", + axisPointer: { + // 默认为直线,可选为:'line' | 'shadow' + type: "shadow" + } + }, + grid: { + left: "0%", + top: 10, + right: "0%", + bottom: "4%", + // true: 数值离DOM盒子的距离 false: 坐标轴离DOM盒子的距离 + containLabel: true + }, + xAxis: [ + { + type: "category", + data: ["领导班子", "海外中国籍", "正式在岗", "海外外籍", "退休返聘", "其他临时", "劳务派遣", "内退"], + axisTick: { + alignWithLabel: true + }, + // 修改坐标值样式 + axisLabel: { + color: "rgba(255, 255, 255, 1)", + fontSize: 10, + rotate: 60, + show: true + }, + axisLine: { + show: false + } + } + ], + yAxis: [ + { + type: "value", + // 修改坐标值样式 + axisLabel: { + color: "rgba(255, 255, 255, 1)", + fontSize: 12 + }, + // 修改坐标轴线样式 + axisLine: { + show: false + }, + axisTick: { + show: false // 不显示坐标轴刻度线 + }, + splitLine: { + lineStyle: { + type: "dashed", + color: "rgba(93,126,158,1)" + } + } + } + ], + series: [ + { + name: "直接访问", + type: "bar", + showBackground: true, + barWidth: "30%", + data: [10, 52, 200, 334, 390, 1030, 220, 210], + // bar 样式修改 + itemStyle: { + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { offset: 0, color: "#3EA1FF" }, + { offset: 0.5, color: "#72CFFF" }, + { offset: 1, color: "#72CFFF" } + ]), + emphasis: { + itemStyle: { + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { offset: 0, color: "#AFD8FF" }, + { offset: 0.7, color: "#AFD8FF" }, + { offset: 1, color: "#AFD8FF" } + ]) + } + } + } + } + ] + }; + // 配置项给实例对象 + myChart.setOption(option); + return myChart; +}; + +export default PersonnelCategoryBar; diff --git a/src/pages/personnelReport/reportScreen/components/frameComp.tsx b/src/pages/personnelReport/reportScreen/components/frameComp.tsx new file mode 100644 index 0000000..648cc10 --- /dev/null +++ b/src/pages/personnelReport/reportScreen/components/frameComp.tsx @@ -0,0 +1,64 @@ +/* + * Author: 黎永顺 + * name: 框架组件 + * Description: + * Date: 2023/5/4 + */ +import React, { FC, useState } from "react"; +import cs from "classnames"; +import styles from "./index.less"; + +interface OwnProps { + position: string; + tabList: Array; + children: any; + titleImg: any; + type?: string; +} + +type Props = OwnProps; +const FrameComp: FC = (props) => { + const { position, children, tabList, titleImg } = props; + const [active, setActive] = useState(0); + + return ( +
+
+
+ + { + position === "right" && +
    + { + _.map(tabList, (item, index) => { + const classes = cs({ + [styles["liActive"]]: index === active + }); + return
  • setActive(index)} className={classes}>{item}
  • ; + }) + } +
+ } +
+ More +
+ { + position === "bottom" && +
    + { + _.map(tabList, (item, index) => { + const classes = cs({ + [styles["liActive"]]: index === active + }); + return
  • setActive(index)} className={classes}>{item}
  • ; + }) + } +
+ } + {children} +
+ ); +}; + +export default FrameComp; diff --git a/src/pages/personnelReport/reportScreen/components/index.less b/src/pages/personnelReport/reportScreen/components/index.less new file mode 100644 index 0000000..661c4ca --- /dev/null +++ b/src/pages/personnelReport/reportScreen/components/index.less @@ -0,0 +1,125 @@ +.topPageWrapper { + height: 1.25rem; + position: relative; + background: url("../../../../assets/images/head_bg1.png") no-repeat; + background-size: 100% 100%; + + .title { + text-align: center; + line-height: 0.875rem; + position: relative; + + .updateTime { + color: #fff; + line-height: 0; + margin-top: 0.0625rem; + + img { + width: 0.225rem; + margin-right: .2rem; + } + } + + img { + width: 4.475rem; + } + } + + .logoWrapper, .showTime { + position: absolute; + } + + .showTime { + right: 2.375rem; + top: 0.4rem; + text-align: right; + color: #fff; + + .dateDay { + padding: 0 .35rem; + } + } + + .logoWrapper { + top: 0.1rem; + left: 2.375rem; + line-height: 0.875rem; + + img { + width: 1.675rem; + } + } +} + +.mapWrapper { + width: 10.625rem; + height: 8.125rem; + margin-top: 1rem; +} + +.frameWrapper { + background: url("../../../../assets/images/frame_bg.png") no-repeat; + background-size: 100% 100%; + margin-bottom: 0.2rem; + + .headerTop { + height: 0.475rem; + position: relative; + background: url("../../../../assets/images/frame_title_bg.png") no-repeat; + background-size: 100% 100%; + display: flex; + justify-content: flex-end; + + .halfHeaderLeft { + left: 1.45rem !important; + } + + .headerLeft { + position: absolute; + left: 0.7375rem; + top: 0.0625rem; + display: flex; + + img { + height: 0.3rem; + } + } + + .moreWrapper { + color: #fff; + font-size: 0.175rem; + position: absolute; + right: 0.3375rem; + top: 0.1rem; + cursor: pointer; + + img { + width: 0.2375rem; + } + } + + .tabWrapper { + margin-left: 0.775rem; + padding: 0; + } + } + + .tabWrapper { + display: flex; + margin: 0; + padding: 0 0.3375rem; + + li { + padding: 0.1rem 0.175rem; + font-size: 14px; + color: #ACACAC; + cursor: pointer; + } + + li.liActive { + background: url("../../../../assets/images/tabSelected.png") no-repeat; + background-size: 100% 100%; + background-position-y: .125rem; + } + } +} diff --git a/src/pages/personnelReport/reportScreen/components/map.tsx b/src/pages/personnelReport/reportScreen/components/map.tsx new file mode 100644 index 0000000..11bbd11 --- /dev/null +++ b/src/pages/personnelReport/reportScreen/components/map.tsx @@ -0,0 +1,25 @@ +/* + * Author: 黎永顺 + * name: 地图组件 + * Description: + * Date: 2023/5/4 + */ +import React, { FC } from "react"; +import Chart from "@/utils/chart"; +import { mapOptions } from "./options"; +import styles from "./index.less"; + +interface OwnProps { +} + +type Props = OwnProps; +const Map: FC = (props) => { + + return ( +
+ +
+ ); +}; + +export default Map; diff --git a/src/pages/personnelReport/reportScreen/components/options.js b/src/pages/personnelReport/reportScreen/components/options.js new file mode 100644 index 0000000..cbe2bd6 --- /dev/null +++ b/src/pages/personnelReport/reportScreen/components/options.js @@ -0,0 +1,243 @@ +import "echarts/map/js/china"; +// 地图数据 +const mapData = { + citys: [ + { + name: "浙江", + value: [120.15, 29.28, -2], + symbolSize: 2, + itemStyle: { + normal: { + color: "#FFF" + } + } + }, + { + name: "四川", + value: [103.36, 30.65, -5], + symbolSize: 2, + itemStyle: { + normal: { + color: "#FFF" + } + } + }, + { + name: "内蒙古", + value: [112.17, 42.81, -23], + symbolSize: 2, + itemStyle: { + normal: { + color: "#FFF" + } + } + }, + { + name: "江苏", + value: [120.26, 32.54, -1], + symbolSize: 2, + itemStyle: { + normal: { + color: "#FFF" + } + } + }, + { + name: "西藏", + value: [89.13, 30.66, -1], + symbolSize: 2, + itemStyle: { + normal: { + color: "#FFF" + } + } + } + ] +}; +export const salaryData = { + header: ["业务板块", "二级机构", "人员类型", "起始时间", "截止时间", "发薪人数", "固定薪酬", "浮动薪酬", "薪酬合计", "薪酬固浮化"], + data: [ + ["国内工程", "建设集团", "领导班子", "2023.1", "2023.1", "23", "20,000", "20,000", "20,000", "20,000"], + ["国内工程", "建设集团", "领导班子", "2023.1", "2023.1", "23", "20,000", "20,000", "20,000", "20,000"], + ["国内工程", "建设集团", "领导班子", "2023.1", "2023.1", "23", "20,000", "20,000", "20,000", "20,000"], + ["国内工程", "建设集团", "领导班子", "2023.1", "2023.1", "23", "20,000", "20,000", "20,000", "20,000"], + ["国内工程", "建设集团", "领导班子", "2023.1", "2023.1", "23", "20,000", "20,000", "20,000", "20,000"], + ["国内工程", "建设集团", "领导班子", "2023.1", "2023.1", "23", "20,000", "20,000", "20,000", "20,000"], + ["国内工程", "建设集团", "领导班子", "2023.1", "2023.1", "23", "20,000", "20,000", "20,000", "20,000"], + ["国内工程", "建设集团", "领导班子", "2023.1", "2023.1", "23", "20,000", "20,000", "20,000", "20,000"] + ] +}; + +export const mapOptions = (params) => ({ + title: { + show: false, + text: "全国物流输送图", + left: "center", + textStyle: { + color: "#fff" + } + }, + legend: { + show: false + }, + geo: [ + { + nameMap: { + China: "中国" + }, + map: "china", + zlevel: 5, + label: { + normal: { + show: true, + formatter: function (params) { + //圆环显示文字 + return params.name === "江苏" || params.name === "浙江" || params.name === "四川" || params.name === "内蒙古" || params.name === "西藏" ? params.name : ""; + }, + fontSize: 12, + color: "#FFF" + }, + emphasis: { + show: true, + color: "#FFF" + } + }, + zoom: 1.2, + itemStyle: { + normal: { + borderColor: "rgba(148,224,250, .5)", //区域边框颜色 + areaColor: "rgba(82,164,238,.1)", //区域颜色 + borderWidth: 1, //区域边框宽度 + shadowBlur: 5, + shadowColor: "rgba(3,113,173,.7)" + }, + emphasis: { + borderColor: "rgba(148,224,250, .5)", + areaColor: "rgba(6,89,176,.3)", + borderWidth: 2.5, + shadowBlur: 5, + shadowColor: "rgba(3,113,173,.5)" + } + } + }, + { + nameMap: { + China: "中国" + }, + map: "china", + zlevel: 4, + top: "11%", + label: { + emphasis: { + show: false + } + }, + zoom: 1.2, + itemStyle: { + normal: { + // borderColor: 'rgba(255,209,163, .5)', //区域边框颜色 + areaColor: "rgba(73,86,166,.1)", //区域颜色 + borderWidth: 0.5, //区域边框宽度 + shadowBlur: 5, + shadowColor: "rgba(107,91,237,.7)" + }, + emphasis: { + borderColor: "transparent", + areaColor: "transparent", + borderWidth: 1, + shadowBlur: 5, + shadowColor: "transparent" + } + } + }, + { + nameMap: { + China: "中国" + }, + map: "china", + zlevel: 3, + top: "12%", + label: { + emphasis: { + show: false + } + }, + zoom: 1.2, + itemStyle: { + normal: { + areaColor: "rgba(73,86,166,.1)", //区域颜色 + borderWidth: 0.5, //区域边框宽度 + shadowBlur: 5, + shadowColor: "rgba(107,91,237,.7)" + }, + emphasis: { + borderColor: "transparent", + areaColor: "transparent", + borderWidth: 1, + shadowBlur: 5, + shadowColor: "transparent" + } + } + }, + { + nameMap: { + China: "中国" + }, + map: "china", + zlevel: 2, + top: "13%", + label: { + emphasis: { + show: false + } + }, + zoom: 1.2, + itemStyle: { + normal: { + // borderColor: 'rgba(255,209,163, .5)', //区域边框颜色 + areaColor: "rgba(73,86,166,.1)", //区域颜色 + borderWidth: 0.5, //区域边框宽度 + shadowBlur: 5, + shadowColor: "rgba(107,91,237,.7)" + }, + emphasis: { + borderColor: "transparent", + areaColor: "transparent", + borderWidth: 1, + shadowBlur: 5, + shadowColor: "transparent" + } + } + } + ], + series: [ + { + name: "地点", + type: "custom", + coordinateSystem: "geo", + zlevel: 12, + renderItem(params, api) { + //具体实现自定义图标的方法 + return { + type: "image", + style: { + image: require("../../../../assets/images/zs.png"), + x: api.coord([ + mapData.citys[params.dataIndex].value[0], + mapData.citys[params.dataIndex].value[1] + ])[0], + y: api.coord([ + mapData.citys[params.dataIndex].value[0], + mapData.citys[params.dataIndex].value[1] + ])[1] + } + }; + }, + data: mapData.citys + } + ] +}); +export const userOptions = (params = {}) => ({ + header: params.header, + data: params.data +}); diff --git a/src/pages/personnelReport/reportScreen/components/topPage.tsx b/src/pages/personnelReport/reportScreen/components/topPage.tsx new file mode 100644 index 0000000..01930f7 --- /dev/null +++ b/src/pages/personnelReport/reportScreen/components/topPage.tsx @@ -0,0 +1,53 @@ +/* + * Author: 黎永顺 + * name: 大屏头部组件 + * Description: + * Date: 2023/5/4 + */ +import React, { FC, useEffect, useRef, useState } from "react"; +import { formatTime } from "@/utils/common"; +import styles from "./index.less"; + +interface OwnProps { +} + +type Props = OwnProps; +const weekday = ["星期天", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"]; +const TopPage: FC = (props) => { + const [timeStr, setTimeStr] = useState({}); + const timing = useRef(null); + useEffect(() => { + setTimingFn(); + return () => { + timing.current = null; + }; + }, []); + + const setTimingFn = () => { + timing.current = setInterval(() => { + let dateYear = formatTime(new Date(), "yyyy-MM-dd"); + let dateDay = formatTime(new Date(), "HH:mm:ss"); + let dateWeek = weekday[new Date().getDay()]; + setTimeStr({ dateYear, dateDay, dateWeek }); + }, 1000); + }; + + return ( +
+ +
+ +
+ + 数据更新时间:16:02:09 +
+
+ {timeStr?.dateYear}{timeStr?.dateDay}{timeStr?.dateWeek} +
+ ); +}; + +export default TopPage; diff --git a/src/pages/personnelReport/reportScreen/index.less b/src/pages/personnelReport/reportScreen/index.less new file mode 100644 index 0000000..b0dd1bd --- /dev/null +++ b/src/pages/personnelReport/reportScreen/index.less @@ -0,0 +1,115 @@ +.screenWrapperRoot { + width: 100%; + height: 100%; + overflow-y: auto; + + .screenWrapper { + line-height: 1.15; + min-height: 100vh; + background: url("../../../assets/images/pageBg.png") top center no-repeat; + background-size: 100% 100%; + + .screenMain { + display: flex; + min-width: 1024px; + max-width: 1920px; + margin: 0 auto; + padding: 0 0.6875rem; + + .screenColCenter { + flex: 5 1; + margin: 0 0.125rem; + position: relative; + + .cardWrapper { + display: flex; + width: 100%; + position: absolute; + top: 0; + left: 0; + z-index: 99; + padding: 0.25rem 0.125rem 0; + + & > li:not(:last-child) { + margin-right: 0.275rem; + } + + & > li { + flex: 1; + min-height: 1.1625rem; + background: url("../../../assets/images/card_bg.png") no-repeat; + background-size: 100% 100%; + display: flex; + align-items: center; + justify-content: center; + + img { + width: 0.725rem; + margin-right: 0.2rem; + } + + .cardInner { + display: flex; + flex-direction: column; + color: #FFF; + min-width: 1.125rem; + text-align: center; + + span:first-child { + font-size: 0.275rem; + margin-bottom: 0.1rem; + } + + span:last-child { + font-size: 0.2rem; + } + } + } + } + } + } + + .screenCol { + flex: 3 1; + + .personnelCate, .ageStruct, .degreeStructure, .gradAnalysis { + height: 3.875rem; + padding: 0 0.3375rem; + } + } + + .screenFooter { + display: flex; + min-width: 1024px; + max-width: 1920px; + margin: 0 auto; + padding: 0 0.6875rem; + + & > div:first-child { + margin-right: 0.125rem; + } + + .screenFooterCol { + flex: 1; + + .payrollStatistics { + padding-bottom: 0.2375rem !important; + + :global { + .dv-scroll-board { + .rows .row-item, .header { + font-size: 0.15rem !important; + } + } + } + } + + .payrollStatistics, .personnelChangeStatistics { + height: 3.5rem; + padding: 0 0.3375rem; + } + } + } + } +} + diff --git a/src/pages/personnelReport/reportScreen/index.tsx b/src/pages/personnelReport/reportScreen/index.tsx new file mode 100644 index 0000000..1a82add --- /dev/null +++ b/src/pages/personnelReport/reportScreen/index.tsx @@ -0,0 +1,147 @@ +/* + * Author: 黎永顺 + * name: 报表大屏 + * Description: + * Date: 2023/5/4 + */ +import React, { FC, useEffect } from "react"; +// @ts-ignore +import echarts from "echarts"; +import "echarts-gl"; +import TopPage from "./components/topPage"; +import Map from "./components/map"; +import FrameComp from "./components/frameComp"; +import PersonnelCategoryBar from "./components/echarts/personnelCategoryBar"; +import AgeStructureBar from "./components/echarts/ageStructureBar"; +import DegreeStructurePie from "./components/echarts/degreeStructurePie"; +import GradAnalysisPie from "./components/echarts/gradAnalysisPie"; +import ChangeStatisticsBar from "./components/echarts/changeStatisticsBar"; +import { salaryData, userOptions } from "./components/options"; +// @ts-ignore +import { ScrollBoard } from "@jiaminghi/data-view-react"; +import styles from "./index.less"; + +interface OwnProps { +} + +type Props = OwnProps; +const Index: FC = (props) => { + useEffect(() => { + const personnelCategoryBar = PersonnelCategoryBar(echarts); + const ageStructureBar = AgeStructureBar(echarts); + const degreeStructurePie = DegreeStructurePie(echarts); + const gradAnalysisPie = GradAnalysisPie(echarts); + const changeStatisticsBar = ChangeStatisticsBar(echarts); + // 屏幕缩放对chart图表进行自适应处理,调用实例的resize方法 + window.onresize = () => { + personnelCategoryBar.resize(); + ageStructureBar.resize(); + degreeStructurePie.resize(); + gradAnalysisPie.resize(); + changeStatisticsBar.resize(); + }; + return () => { + }; + }, []); + + return ( +
+
+ +
+
+ +
+ + +
+ +
+
+
    +
  • + +
    + 8,29,000 + 销售金额 +
    +
  • +
  • + +
    + 3,29,000 + 项目验收 +
    +
  • +
  • + +
    + 12,000 + 月均工资 +
    +
  • +
  • + +
    + 98% + 任务达成率 +
    +
  • +
+ +
+
+ +
+
+ +
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ +
+
+
+
+ ); +}; + +export default Index; diff --git a/src/pages/personnelReport/talentProfitTable/index.less b/src/pages/personnelReport/talentProfitTable/index.less new file mode 100644 index 0000000..e88969a --- /dev/null +++ b/src/pages/personnelReport/talentProfitTable/index.less @@ -0,0 +1,114 @@ +.profitWrapper { + display: flex; + width: 100%; + height: 100%; + background: #e5e5e5; + padding: 8px; + overflow: hidden; + + .profitLeft { + display: flex; + flex-direction: column; + width: 580px; + overflow: hidden; + + & > :not(:last-child) { + margin-bottom: 16px; + } + + & > div:first-child { + & > div { + height: 100% !important; + } + } + + & > div { + flex: 1; + background: #FFF; + position: relative; + padding: 40px 10px 8px; + overflow: hidden; + + .title { + display: inline-block; + position: absolute; + left: 1%; + top: 2%; + font-size: 16px; + font-weight: 700; + } + + :global { + .ant-tabs { + height: 100%; + line-height: normal; + + .ant-tabs-content-holder { + flex: 1; + + .ant-tabs-content { + height: 100%; + + .ant-tabs-tabpane { + overflow-y: auto; + + & > div { + height: 100% !important; + } + } + } + } + } + } + } + } + + .profitRight { + flex: 1; + position: relative; + margin-left: 16px; + display: flex; + flex-direction: column; + overflow: hidden; + + & > div:first-child { + margin-bottom: 16px; + } + + & > div { + background: #fff; + flex: 1; + padding: 40px 10px 8px; + position: relative; + height: 100px; + + :global { + .ant-tabs { + height: 100%; + line-height: normal; + + .ant-tabs-content-holder { + flex: 1; + + .ant-tabs-content { + height: 100%; + + .ant-tabs-tabpane { + overflow-y: auto; + } + } + } + } + } + + .title { + display: inline-block; + position: absolute; + left: 1%; + top: 2%; + font-size: 16px; + font-weight: 700; + } + } + } +} diff --git a/src/pages/personnelReport/index.tsx b/src/pages/personnelReport/talentProfitTable/index.tsx similarity index 73% rename from src/pages/personnelReport/index.tsx rename to src/pages/personnelReport/talentProfitTable/index.tsx index 18fc776..5244dc9 100644 --- a/src/pages/personnelReport/index.tsx +++ b/src/pages/personnelReport/talentProfitTable/index.tsx @@ -1,29 +1,24 @@ +/* + * Author: 黎永顺 + * name:人才利润报表 + * Description: + * Date: 2023/4/27 + */ import React, { FC } from "react"; +import styles from "./index.less"; import { Avatar, List, Tabs } from "antd"; -import { Column, Funnel, Pie, DualAxes } from "@ant-design/plots"; import { - degreeData, - flowingConfig, - flowingTrendconfig, - genderData, + fixedIncomePerData, + floatingIncomePerData, humanAnalysisconfig, humanEfficiencyRankingData, - institutionConfig, - internData, - linesData, - multiplData, - multipleConfig, newSaleEfficiencyRankingData, - personChangeConfig, - personChangeData, - rankingConfig, - rankingData, saleEfficiencyRankingData, - seniorityConfig, - seniorityData, - trainingData,totalLaborCostConfig, laborCostsPerCapitaConfig, - perCapitaIncomeConfig, fixedIncomePerData, floatingIncomePerData -} from "./constants"; -import styles from "./index.less"; + newSaleEfficiencyRankingData, + perCapitaIncomeConfig, + saleEfficiencyRankingData, + totalLaborCostConfig +} from "@/pages/personnelReport/constants"; +import { Column, DualAxes, Pie } from "@ant-design/plots"; interface OwnProps { } @@ -31,51 +26,42 @@ interface OwnProps { type Props = OwnProps; const index: FC = (props) => { - // @ts-ignore + return ( -
- {/*人才结构表*/} -
-
-
人员流动情况
- +
+
+
+ 人效分析 + {/*@ts-ignore*/} +
-
+
+ 人均收入 - - - - - + + - - + + - - + +
+
+ 人工成本 + + + {/*@ts-ignore*/} + - - + +
-
-
人员流动情况趋势图
- -
-
-
机构人员排名数
- -
- {/*人才利润报表*/} -
+
-
人效分析
- -
-
-
人效分析排名
+ 人效分析排名 = (props) => {
-
团队效能排名
+ 团队效能排名 = (props) => {
-
-
人均收入
- - - - - - - - -
-
-
人工成本
- - - - - - - - -
-
- {/*人员流量表*/} -
-
-
人员异动分析
- -
-
-
详细图表
- -
-
-
培训管理分析
- -
-
-
培训排名
- -
); diff --git a/src/pages/personnelReport/talentStructureTable/components/ageStaticsCharts.ts b/src/pages/personnelReport/talentStructureTable/components/ageStaticsCharts.ts new file mode 100644 index 0000000..4e7a7e4 --- /dev/null +++ b/src/pages/personnelReport/talentStructureTable/components/ageStaticsCharts.ts @@ -0,0 +1,48 @@ +const AgeStaticsPie = (echarts: any) => { + // 实例化对象 + const myChart = echarts.init(document.getElementById("ageStatics")); + // 配置项 + const option = { + legend: { + icon: "circle", + bottom: "0%", + left: "center", + itemGap: 20, + textStyle: { + fontSize: 12,//字体大小 + color: "#787E95"//字体颜色 + } + }, + color: ["#5FCD7B", "#F5CB49", "#7599F5", "#64BFDB"], + series: [ + { + name: "司龄统计", + type: "pie", + radius: ["45%", "60%"], + avoidLabelOverlap: false, + animation: false, + label: { + normal: { + formatter: "{d}%", + padding: [0, -25], + position: "outer" //文字显示在内部,如果在外边把这个去掉就好 + } + }, + labelLine: { + show: false + }, + data: [ + { value: 40, name: "1-3年" }, + { value: 35, name: "3-5年" }, + { value: 20, name: "5-8年" }, + { value: 5, name: "8年以上" } + ] + } + ] + }; + // 配置项给实例对象 + myChart.setOption(option); + return myChart; +}; + +export default AgeStaticsPie; diff --git a/src/pages/personnelReport/talentStructureTable/components/educationStaticsCharts.ts b/src/pages/personnelReport/talentStructureTable/components/educationStaticsCharts.ts new file mode 100644 index 0000000..d426de5 --- /dev/null +++ b/src/pages/personnelReport/talentStructureTable/components/educationStaticsCharts.ts @@ -0,0 +1,56 @@ +const EducationStaticsRedar = (echarts: any) => { + // 实例化对象 + const myChart = echarts.init(document.getElementById("educationStatics")); + // 配置项 + const option = { + tooltip: { + show: true + }, + radar: { + // shape: 'circle', + indicator: [ + { name: "博士及以上", max: 800, axisLabel: { show: true } }, + { name: "硕士", max: 800 }, + { name: "本科", max: 800 }, + { name: "大专", max: 800 }, + { name: "大专一下", max: 800 } + ], + splitArea: { + show: false + }, + axisLine: { + show: false + } + }, + series: [ + { + name: "学历统计", + type: "radar", + data: [ + { + value: [210, 400, 800, 230, 180], + name: "学历统计" + } + ], + label: { + show: false, + formatter: function (params: any) { + return params.value; + } + }, + itemStyle: { //此属性的颜色和下面areaStyle属性的颜色都设置成相同色即可实现 + color: "#4D95E9", + borderColor: "#4D95E9" + }, + areaStyle: { + color: "rgba(77,149,233,0.26)" + } + } + ] + }; + // 配置项给实例对象 + myChart.setOption(option); + return myChart; +}; + +export default EducationStaticsRedar; diff --git a/src/pages/personnelReport/talentStructureTable/components/genderStaticsCharts.ts b/src/pages/personnelReport/talentStructureTable/components/genderStaticsCharts.ts new file mode 100644 index 0000000..34d336a --- /dev/null +++ b/src/pages/personnelReport/talentStructureTable/components/genderStaticsCharts.ts @@ -0,0 +1,44 @@ +const GenderStaticsPie = (echarts: any) => { + // 实例化对象 + const myChart = echarts.init(document.getElementById("genderStatics")); + // 配置项 + const option = { + legend: { + icon: "circle", + bottom: "0%", + left: "center", + itemGap: 20, + textStyle: { + fontSize: 12,//字体大小 + color: "#787E95"//字体颜色 + } + }, + color: ["#A6BEF3", "#205CE1"], + grid: { + bottom: "10%", + containLabel: true + }, + series: [ + { + name: "性别统计", + type: "pie", + startAngle:-180, + radius: [40, 70], + roseType: "radius", + animation: false, + label: { + show: false + }, + data: [ + { value: 70, name: "男" }, + { value: 30, name: "女" } + ] + } + ] + }; + // 配置项给实例对象 + myChart.setOption(option); + return myChart; +}; + +export default GenderStaticsPie; diff --git a/src/pages/personnelReport/talentStructureTable/components/index.less b/src/pages/personnelReport/talentStructureTable/components/index.less new file mode 100644 index 0000000..071bab9 --- /dev/null +++ b/src/pages/personnelReport/talentStructureTable/components/index.less @@ -0,0 +1,141 @@ +.structureCardWrapper { + width: 100%; + height: 100%; + overflow: hidden; + display: flex; + flex-direction: column; + + .top { + display: flex; + + & > span { + flex: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 14px; + color: #333333; + font-weight: 700; + } + + ul { + display: flex; + + li:last-child { + margin-left: 16px; + } + + li.active { + color: #333333; + } + + li { + font-size: 12px; + color: #D4D4D4; + cursor: pointer; + } + } + } + + .center { + display: flex; + align-items: flex-end; + + & > div:first-child { + & > span:first-child { + font-size: 24px; + letter-spacing: 0; + font-weight: 700; + } + + & > span:last-child { + font-size: 14px; + color: #D4D4D4; + font-weight: 400; + } + } + + & > div { + flex: 1; + } + + .charts { + height: 62px; + } + } + + .bottom { + display: flex; + justify-content: space-between; + align-items: center; + border-top: 0.5px solid #e5e5e5; + padding-top: 10px; + + & > span { + & > span:first-child { + font-size: 12px; + color: #D4D4D4; + font-weight: 400; + margin-right: 8px; + } + + & > span:last-child { + font-size: 12px; + color: #333333; + font-weight: 400; + } + } + } +} + +.structureFrameWrapper { + width: 100%; + min-height: 100px; + display: flex; + flex-direction: column; + background: #fff; + padding: 16px; + border-radius: 5px; + + .header { + display: flex; + align-items: center; + border-bottom: .5px solid #e5e5e5; + padding-bottom: 14px; + + img { + width: 28px; + } + + span { + display: inline-block; + font-size: 18px; + color: #333333; + letter-spacing: 0; + font-weight: 700; + margin-left: 8px; + } + } + + .tabBox { + margin: 0; + display: flex; + justify-content: flex-end; + padding: 20px 32px 0 0; + + li:last-child { + margin-left: 16px; + } + + li.active { + color: #1E66F6; + } + + li { + font-size: 14px; + color: #999999; + cursor: pointer; + font-weight: 550; + } + } +} diff --git a/src/pages/personnelReport/talentStructureTable/components/internStaticsCharts.ts b/src/pages/personnelReport/talentStructureTable/components/internStaticsCharts.ts new file mode 100644 index 0000000..39ecef5 --- /dev/null +++ b/src/pages/personnelReport/talentStructureTable/components/internStaticsCharts.ts @@ -0,0 +1,85 @@ +const InternStaticsBar = (echarts: any) => { + // 实例化对象 + const myChart = echarts.init(document.getElementById("internStatics")); + // 配置项 + const option = { + xAxis: [ + { + type: "value", + axisLabel: { + formatter: "{value} " + }, + splitLine: { + show: true, + lineStyle: { + type: "dashed", + color: "#1D3039" + } + }, + axisTick: { + show: false + }, + axisLine: { + show: false, + lineStyle: { + fontSize: 12, + color: "#D4D4D4" + } + } + } + ], + grid: { + top: "10%", + left: "5%", + right: "5%", + bottom: "3%", + containLabel: true + }, + yAxis: [ + { + type: "category", + offset: 0, + data: ["项目实习生", "销售实习生", "技术实习生", "大区实习生", "客服实习生"], + axisPointer: { + type: "shadow" + }, + axisLine: { + //这是x轴文字颜色 + show: false + }, + axisTick: { + show: false + }, + axisLabel: { + color: "#787E95", + fontSize: 10 + } + } + ], + series: [ + { + data: [34, 19, 15, 32, 11], + type: "bar", + barWidth: "25%", + realtimeSort: true, + yAxisIndex: 0, + itemStyle: { + color: "#4CAEFF" //设定单个柱子颜色 + }, + label: { + normal: { + show: true, + position: "right", + fontWeight: "bold", + fontSize: 12 + } + } + } + ] + }; + // 配置项给实例对象 + myChart.setOption(option); + return myChart; +}; + +export default InternStaticsBar; diff --git a/src/pages/personnelReport/talentStructureTable/components/lineStaticsCharts.ts b/src/pages/personnelReport/talentStructureTable/components/lineStaticsCharts.ts new file mode 100644 index 0000000..30c08a6 --- /dev/null +++ b/src/pages/personnelReport/talentStructureTable/components/lineStaticsCharts.ts @@ -0,0 +1,50 @@ +const LineStaticsPie = (echarts: any) => { + // 实例化对象 + const myChart = echarts.init(document.getElementById("lineStatics")); + // 配置项 + const option = { + legend: { + icon: "circle", + bottom: "0%", + left: "center", + itemGap: 20, + textStyle: { + fontSize: 12,//字体大小 + color: "#787E95"//字体颜色 + } + }, + color: ["#61D485", "#F4CB48", "#7499F5", "#65BFDC", "#ED9C5F"], + series: [ + { + name: "司龄统计", + type: "pie", + radius: ["45%", "60%"], + center: ['50%', '41%'], + avoidLabelOverlap: false, + animation: false, + label: { + normal: { + formatter: "{d}%", + padding: [0, -25], + position: "outer" //文字显示在内部,如果在外边把这个去掉就好 + } + }, + labelLine: { + show: false + }, + data: [ + { value: 40, name: "项目" }, + { value: 15, name: "市场" }, + { value: 20, name: "销售" }, + { value: 15, name: "支撑" }, + { value: 10, name: "销售1" } + ] + } + ] + }; + // 配置项给实例对象 + myChart.setOption(option); + return myChart; +}; + +export default LineStaticsPie; diff --git a/src/pages/personnelReport/talentStructureTable/components/monthlyTrendsCharts.ts b/src/pages/personnelReport/talentStructureTable/components/monthlyTrendsCharts.ts new file mode 100644 index 0000000..0f412a9 --- /dev/null +++ b/src/pages/personnelReport/talentStructureTable/components/monthlyTrendsCharts.ts @@ -0,0 +1,149 @@ +const MonthlyTrendsBar = (echarts: any) => { + // 实例化对象 + const myChart = echarts.init(document.getElementById("monthlyTrendsCharts")); + // 配置项 + const option = { + tooltip: { + // 坐标轴指示器,坐标轴触发有效 + trigger: "axis", + axisPointer: { + // 默认为直线,可选为:'line' | 'shadow' + type: "shadow" + } + }, + grid: { + top: "10%", + left: "2%", + right: "2%", + bottom: "3%", + containLabel: true + }, + xAxis: [ + { + type: "category", + data: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"], + axisTick: { + alignWithLabel: true, + show: false + }, + // 修改坐标值样式 + axisLabel: { + color: "#B8B8B8", + fontSize: 16, + show: true + }, + axisLine: { + show: false + } + } + ], + yAxis: [ + { + type: "value", + name: "人", + // 修改坐标值样式 + axisLabel: { + color: "#787E95", + fontSize: 14 + // formatter: '{value} 人' + }, + nameTextStyle: { + color: "#787E95", + fontSize: 16 + }, + // 修改坐标轴线样式 + axisLine: { + show: true, + lineStyle: { + color: "transparent" + } + }, + axisTick: { + show: false // 不显示坐标轴刻度线 + }, + splitLine: { + lineStyle: { + type: "dashed", + color: "rgba(93,126,158,1)" + } + } + } + ], + series: [ + { + name: "入职", + type: "bar", + showBackground: false, + barWidth: "8%", + barGap: "100%", + data: [250, 250, 320, 410, 280, 320, 405, 405, 320, 250, 280, 405], + // bar 样式修改 + itemStyle: { + barBorderRadius: 5, + color: "#0FC482" + } + }, + { + name: "离职", + type: "bar", + showBackground: false, + barWidth: "8%", + data: [370, 370, 260, 110, 390, 280, 110, 110, 370, 260, 390, 110], + // bar 样式修改 + itemStyle: { + barBorderRadius: 5, + color: "#FFA500" + } + }, + { + name: "在职", + type: "bar", + showBackground: false, + barWidth: "8%", + data: [310, 310, 80, 280, 310, 70, 280, 280, 80, 280, 310, 280], + // bar 样式修改 + itemStyle: { + barBorderRadius: 5, + color: "#FFD600" + } + }, + { + name: "离职", + type: "line", + yAxisIndex: 0, + data: [370, 370, 260, 110, 390, 280, 110, 110, 370, 260, 390, 110], + itemStyle: { + normal: { + color: "#FFD700", + lineStyle: { + color: "#FFD700" + } + } + }, + areaStyle: { + normal: { + color: {//分隔区域的颜色 + x0: 0, + y0: 0, + x2: 0, + y2: 1, + colorStops: [{ + offset: 0, + color: "#FFD700" // 0% 处的颜色 + }, { + offset: 1, + color: "rgba(255,215,0,0)" // 100% 处的颜色;中间还可以设置50%、20%、70%时的颜色 + }], + globalCoord: false // 缺省为 false,以确保上面的x,y,x2,y2表示的是百分比 + } + } + } + } + ] + }; + // 配置项给实例对象 + myChart.setOption(option); + return myChart; +}; + +export default MonthlyTrendsBar; diff --git a/src/pages/personnelReport/talentStructureTable/components/rankingCharts.ts b/src/pages/personnelReport/talentStructureTable/components/rankingCharts.ts new file mode 100644 index 0000000..4b4a2e0 --- /dev/null +++ b/src/pages/personnelReport/talentStructureTable/components/rankingCharts.ts @@ -0,0 +1,182 @@ +const RankingBar = (echarts: any) => { + // 实例化对象 + const colorList = ["#1E66F6", "#70CDFF", "#00C48B", "#FFD700", "#1E66F6", "#70CDFF", "#00C48B", "#FFD700"]; + const myChart = echarts.init(document.getElementById("rankingCharts")); + // 配置项 + const option = { + xAxis: [ + { + type: "value", + axisLabel: { + formatter: "{value} " + }, + splitLine: { + show: true, + lineStyle: { + type: "dashed", + color: "#1D3039" + } + }, + axisTick: { + show: false + }, + axisLine: { + show: false, + lineStyle: { + fontSize: 12, + color: "#D4D4D4" + } + } + } + ], + grid: { + top: "10%", + left: "5%", + right: "5%", + bottom: "3%", + containLabel: true + }, + yAxis: [ + { + type: "category", + offset: 0, + inverse: true, //升序 + // axisLabel: { + // color: "#787E95", + // fontSize: 16 + // }, + data: ["契约锁", "泛微北京", "泛微上海", "产品研发中心", "泛微华南一部", "泛微广州一部", "泛微杭州", "EBU事业一部"], + axisPointer: { + type: "shadow" + }, + axisLine: { + //这是x轴文字颜色 + show: false + }, + axisTick: { + show: false + }, + axisLabel: { + color: "#787E95", + fontSize: 14, + formatter: function (value: any, index: number) { + if (index == 0) { + return "{one|" + "} {a| " + value + "}"; + } else if (index == 1) { + return "{two|" + "} {a|" + value + "}"; + } else if (index == 2) { + return "{three|" + "} {a|" + value + "}"; + } + return value; + }, + rich: { + a: { + color: "#787E95", + fontSize: 14 + }, + // 第一名 + one: { + width: 40, + height: 20, + margin: 10, + align: "center", + backgroundColor: { + image: require("../../../../assets/images/one.png") + } + }, + two: { + width: 40, + height: 20, + margin: 10, + align: "center", + backgroundColor: { + image: require("../../../../assets/images/two.png") + } + }, + three: { + width: 40, + height: 20, + marginRight: 20, + align: "center", + backgroundColor: { + image: require("../../../../assets/images/three.png") + } + } + } + } + }, + { + type: "category", + data: [4096, 1412, 1337, 788, 788, 347, 332, 291], + inverse: true,//数组翻转显示 + axisTick: { + alignWithLabel: true, + show: false + }, + axisLine: { + show: false//不显示y轴的线 + }, + axisLabel: { + textStyle: { + fontSize: 16, + color: function (params: any, index: any) { + return colorList[index]; + } + } + } + } + ], + series: [ + { + data: _.map([4096, 1412, 1337, 788, 788, 347, 332, 291], (it, idx) => ({ + value: it, + itemStyle: { + color: colorList[idx], //设定单个柱子颜色 + borderColor: colorList[idx], //设定单个柱子边框颜色 + barBorderRadius: [10, 10, 10, 10] + } + })), + type: "bar", + barWidth: "20%", + realtimeSort: true, + yAxisIndex: 0, + label: { + normal: { + show: false, + position: "right", + fontWeight: "bold", + fontSize: 14 + } + }, + showBackground: true, + backgroundStyle: { + color: "rgba(220, 220, 220, 0.8)", + barBorderRadius: [10, 10, 10, 10] + } + }, + { + name: "", + type: "bar", + yAxisIndex: 1,//使两个柱状图重合的效果 + barWidth: "25%", + data: _.map([5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000], (it, idx) => ({ + value: it, + itemStyle: { + color: "none", + borderColor: colorList[idx], + borderWidth: 2, + barBorderRadius: [10, 10, 10, 10] + } + })), + label: { + show: false + } + } + ] + }; + // 配置项给实例对象 + myChart.setOption(option); + return myChart; +}; + +export default RankingBar; diff --git a/src/pages/personnelReport/talentStructureTable/components/structureCard.tsx b/src/pages/personnelReport/talentStructureTable/components/structureCard.tsx new file mode 100644 index 0000000..d84b70a --- /dev/null +++ b/src/pages/personnelReport/talentStructureTable/components/structureCard.tsx @@ -0,0 +1,68 @@ +/* + * Author: 黎永顺 + * name: 卡片组件 + * Description: + * Date: 2023/5/5 + */ +import React, { FC } from "react"; +import { TinyArea } from "@ant-design/plots"; +import styles from "./index.less"; +import cs from "classnames"; + +interface OwnProps { + title: string; + active: number; + number: string; + unit: string; + color: string; + tabList: Array; +} + +type Props = OwnProps; +const config = { + height: 60, + autoFit: false, + data: [264, 417, 438, 887, 309, 397, 550], + smooth: true, + areaStyle: { + fill: "#d6e3fd" + }, + tooltip: { + showCrosshairs: false, + showContent: false + } +}; +const StructureCard: FC = (props) => { + const { active, tabList, title, number, unit, color } = props; + + return ( +
+
+ {title} +
    + { + _.map(tabList, (it, idx) => { + const classes = cs({ + [styles["active"]]: idx === active + }); + return
  • {it}
  • ; + }) + } +
+
+
+
{number}{unit}
+
+
+
+ 同比:4.75% + 环比:1.75% +
+
+ ); +}; + +export default StructureCard; diff --git a/src/pages/personnelReport/talentStructureTable/components/structureFrame.tsx b/src/pages/personnelReport/talentStructureTable/components/structureFrame.tsx new file mode 100644 index 0000000..49366de --- /dev/null +++ b/src/pages/personnelReport/talentStructureTable/components/structureFrame.tsx @@ -0,0 +1,48 @@ +/* + * Author: 黎永顺 + * name: 人才结构表框组件 + * Description: + * Date: 2023/5/5 + */ +import React, { FC, useState } from "react"; +import styles from "./index.less"; +import cs from "classnames"; + +interface OwnProps { + title: string; + children: any; + tabList?: Array; +} + +type Props = OwnProps; + +const StructureFrame: FC = (props) => { + const { children, tabList, title } = props; + const [active, setActive] = useState(0); + + return ( +
+
+ + {title} +
+ { + !_.isEmpty(tabList) && +
    + { + _.map(tabList, (it, idx) => { + const classes = cs({ + [styles["active"]]: idx === active + }); + return
  • setActive(idx)}>{it}
  • ; + }) + } +
+ } + {children} +
+ ); +}; + +export default StructureFrame +; diff --git a/src/pages/personnelReport/talentStructureTable/index.less b/src/pages/personnelReport/talentStructureTable/index.less new file mode 100644 index 0000000..6f6e2d1 --- /dev/null +++ b/src/pages/personnelReport/talentStructureTable/index.less @@ -0,0 +1,111 @@ +.structureWrapper { + display: flex; + flex-direction: column; + width: 100%; + height: 100%; + background: #e5e5e5; + overflow: auto; + + .structureColumn_1 { + display: flex; + margin: 0 0 10px; + padding: 0; + + & > li:not(:last-child) { + margin-right: 16px; + } + + & > li { + flex: 1; + border-radius: 5px; + background: #FFF; + max-height: 160px; + min-height: 100px; + padding: 16px; + } + } + + .structureColumn_2 { + margin-bottom: 10px; + + .multipleCharts { + height: 330px; + display: flex; + + & > div:not(:last-child) { + div { + border-right: .5px solid #e5e5e5; + } + } + + & > div { + flex: 1; + display: flex; + flex-direction: column; + + span { + display: inline-block; + width: 100%; + text-align: center; + font-size: 16px; + color: #333333; + font-weight: 700; + margin-top: 20px; + } + + div { + flex: 1; + } + } + } + } + + .structureColumn_3 { + display: flex; + + & > div:first-child { + flex: 4 1; + margin-right: 16px; + } + + & > div:last-child { + flex: 2 1; + } + + .monthlyTrendsChartsBox { + width: 100%; + min-height: 438px; + max-height: 438px; + } + + .rankingChartsBox { + width: 100%; + min-height: 480px; + max-height: 480px; + height: 480px; + + :global { + .dv-capsule-chart { + width: 100%; + height: 100%; + + .capsule-container { + .unit-label { + font-size: 12px; + color: #D4D4D4; + text-align: right; + font-weight: 400; + } + } + + .label-column { + font-size: 16px; + color: #787E95; + text-align: right; + font-weight: 400; + } + } + } + } + } +} diff --git a/src/pages/personnelReport/talentStructureTable/index.tsx b/src/pages/personnelReport/talentStructureTable/index.tsx new file mode 100644 index 0000000..3591afa --- /dev/null +++ b/src/pages/personnelReport/talentStructureTable/index.tsx @@ -0,0 +1,99 @@ +/* + * Author: 黎永顺 + * name: 人才结构表 + * Description: + * Date: 2023/4/27 + */ +import React, { FC, useEffect, useState } from "react"; +// @ts-ignore +import echarts from "echarts"; +// @ts-ignore +import { CapsuleChart } from "@jiaminghi/data-view-react"; +import StructureCard from "./components/structureCard"; +import StructureFrame from "./components/structureFrame"; +import MonthlyTrendsBar from "./components/monthlyTrendsCharts"; +import RankingBar from "./components/rankingCharts"; +import AgeStaticsPie from "./components/ageStaticsCharts"; +import GenderStaticsPie from "./components/genderStaticsCharts"; +import LineStaticsPie from "./components/lineStaticsCharts"; +import InternStaticsBar from "./components/internStaticsCharts"; +import EducationStaticsRedar from "./components/educationStaticsCharts"; +import { structureCardList } from "../constants"; +import styles from "./index.less"; + +interface OwnProps { +} + +type Props = OwnProps; + +const index: FC = (props) => { + const [card, setCard] = useState(structureCardList); + useEffect(() => { + const monthlyTrendsBar = MonthlyTrendsBar(echarts); + const rankingBar = RankingBar(echarts); + const ageStaticsPie = AgeStaticsPie(echarts); + const genderStaticsPie = GenderStaticsPie(echarts); + const lineStaticsPie = LineStaticsPie(echarts); + const internStaticsBar = InternStaticsBar(echarts); + const educationStaticsRedar = EducationStaticsRedar(echarts); + // 屏幕缩放对chart图表进行自适应处理,调用实例的resize方法 + window.onresize = () => { + monthlyTrendsBar.resize(); + rankingBar.resize(); + ageStaticsPie.resize(); + genderStaticsPie.resize(); + lineStaticsPie.resize(); + internStaticsBar.resize(); + educationStaticsRedar.resize(); + }; + return () => { + }; + }, []); + + return ( +
+
    + { + _.map(card, (it, idx) => { + return
  • ; + }) + } +
+
+ +
+
司龄统计 +
+
+
性别统计 +
+
+
学历统计 +
+
+
条线统计 +
+
+
实习生统计 +
+
+
+ +
+
+
+ +
+ +
+
+ +
+ +
+
+
+ ); +}; + +export default index; diff --git a/src/utils/chart.js b/src/utils/chart.js new file mode 100644 index 0000000..13645a7 --- /dev/null +++ b/src/utils/chart.js @@ -0,0 +1,381 @@ +/* + * ECharts 组件基础部分 + * 传入 option 和渲染方式 renderer + * */ + +import React, { PureComponent } from "react"; +import * as echarts from "echarts"; +import "zrender/lib/svg/svg"; +import { debounce } from "./common"; // 一个节流函数 + +export default class Chart extends PureComponent { + constructor(props) { + super(props); + this.state = { + width: "100%", + height: "100%" + }; + this.chart = null; + } + + async componentDidMount() { + // 初始化图表 + await this.initChart(this.el); + // 将传入的配置(包含数据)注入 + this.setOption(this.props.option); + // 监听屏幕缩放,重新绘制 echart 图表 + window.addEventListener("resize", debounce(this.resize, 100)); + } + + componentDidUpdate() { + // 每次更新组件都重置 + this.setOption(this.props.option); + } + + componentWillUnmount() { + // 组件卸载前卸载图表 + this.dispose(); + } + + render() { + const { width, height } = this.state; + + return ( +
(this.el = el)} + style={{ width, height }} + /> + ); + } + + initChart = el => { + // renderer 用于配置渲染方式 可以是 svg 或者 canvas + const renderer = this.props.renderer || "canvas"; + + return new Promise(resolve => { + setTimeout(() => { + this.chart = echarts.init(el, null, { + renderer, + width: "auto", + height: "auto" + }); + resolve(); + }, 0); + }); + }; + setOption = option => { + if (!this.chart) { + return; + } + + const notMerge = this.props.notMerge; + const lazyUpdate = this.props.lazyUpdate; + + this.chart.setOption(option, notMerge, lazyUpdate); + }; + dispose = () => { + if (!this.chart) { + return; + } + + this.chart.dispose(); + this.chart = null; + }; + resize = () => { + this.chart && this.chart.resize(); + }; + getInstance = () => { + return this.chart; + }; +} + +/** + * 绘制3d图 + * @param pieData 总数据 + * @param internalDiameterRatio:透明的空心占比 + * @param distance 视角到主体的距离 + * @param alpha 旋转角度 + * @param pieHeight 立体的高度 + * @param opacity 饼或者环的透明度 + * @param isSameHeight 饼或者环的透明度 + */ +const getPie3D = ( + pieData, + internalDiameterRatio, + distance, + alpha, + pieHeight, + opacity = 1, + isSameHeight +) => { + const series = []; + let sumValue = 0; + let startValue = 0; + let endValue = 0; + let legendData = []; + let legendBfb = []; + const k = 1 - internalDiameterRatio; + pieData.sort((a, b) => { + return b.value - a.value; + }); + // 为每一个饼图数据,生成一个 series-surface 配置 + for (let i = 0; i < pieData.length; i++) { + sumValue += pieData[i].value; + const seriesItem = { + name: + typeof pieData[i].name === "undefined" ? `series${i}` : pieData[i].name, + type: "surface", + parametric: true, + wireframe: { + show: false + }, + pieData: pieData[i], + pieStatus: { + selected: false, + hovered: false, + k: k + }, + center: ["10%", "50%"] + }; + if (typeof pieData[i].itemStyle !== "undefined") { + const itemStyle = {}; + itemStyle.color = + typeof pieData[i].itemStyle.color !== "undefined" + ? pieData[i].itemStyle.color + : opacity; + itemStyle.opacity = + typeof pieData[i].itemStyle.opacity !== "undefined" + ? pieData[i].itemStyle.opacity + : opacity; + seriesItem.itemStyle = itemStyle; + } + series.push(seriesItem); + } + + // 使用上一次遍历时,计算出的数据和 sumValue,调用 getParametricEquation 函数, + // 向每个 series-surface 传入不同的参数方程 series-surface.parametricEquation,也就是实现每一个扇形。 + legendData = []; + legendBfb = []; + for (let i = 0; i < series.length; i++) { + endValue = startValue + series[i].pieData.value; + series[i].pieData.startRatio = startValue / sumValue; + series[i].pieData.endRatio = endValue / sumValue; + series[i].parametricEquation = getParametricEquation( + series[i].pieData.startRatio, + series[i].pieData.endRatio, + false, + false, + k, + !isSameHeight && series[i].pieData.value + ); + startValue = endValue; + const bfb = fomatFloat(series[i].pieData.value / sumValue, 4); + legendData.push({ + name: series[i].name, + value: bfb + }); + legendBfb.push({ + name: series[i].name, + value: bfb + }); + } + const boxHeight = getHeight3D(series, pieHeight); // 通过pieHeight设定3d饼/环的高度,单位是px + // 准备待返回的配置项,把准备好的 legendData、series 传入。 + const option = { + legend: { + show: false, + data: legendData, + orient: "vertical", + left: 10, + top: 10, + itemGap: 10, + textStyle: { + color: "#A1E2FF" + }, + icon: "circle", + formatter: function (param) { + const item = legendBfb.filter((item) => item.name === param)[0]; + const bfs = fomatFloat(item.value * 100, 2) + "%"; + return `${item.name} ${bfs}`; + } + }, + labelLine: { + show: true, + lineStyle: { + color: "#fff" + } + }, + label: { + show: true, + position: "outside", + formatter: "{b} \n{c} {d}%" + }, + tooltip: { + backgroundColor: "#033b77", + borderColor: "#21f2c4", + textStyle: { + color: "#fff", + fontSize: 13 + }, + formatter: (params) => { + if ( + params.seriesName !== "mouseoutSeries" && + params.seriesName !== "信用评价" + ) { + // console.log(option.series, params.seriesName, "option.series[params.seriesIndex].pieData"); + const bfb = ( + (option.series[params.seriesIndex].pieData.endRatio - + option.series[params.seriesIndex].pieData.startRatio) * + 100 + ).toFixed(2); + return ( + // ${params.seriesName} + `数量
` + + `` + + `${option.series[params.seriesIndex].pieData.value}(${bfb}%)` + ); + } + } + }, + xAxis3D: { + min: -1, + max: 1 + }, + yAxis3D: { + min: -1, + max: 1 + }, + zAxis3D: { + min: -1, + max: 1 + }, + grid3D: { + show: false, + left: "18%", + boxHeight: !isSameHeight ? boxHeight : 15, // 圆环的高度 + viewControl: { + // 3d效果可以放大、旋转等,请自己去查看官方配置 + alpha, // 角度 + distance, // 调整视角到主体的距离,类似调整zoom + rotateSensitivity: 0, // 设置为0无法旋转 + zoomSensitivity: 0, // 设置为0无法缩放 + panSensitivity: 0, // 设置为0无法平移 + autoRotate: false // 自动旋转 + } + }, + series: series + }; + return option; +}; + +/** + * 生成扇形的曲面参数方程,用于 series-surface.parametricEquation + */ +const getParametricEquation = ( + startRatio, + endRatio, + isSelected, + isHovered, + k, + h = 1 +) => { + // 计算 + const midRatio = (startRatio + endRatio) / 2; + const startRadian = startRatio * Math.PI * 2; + const endRadian = endRatio * Math.PI * 2; + const midRadian = midRatio * Math.PI * 2; + // 如果只有一个扇形,则不实现选中效果。 + if (startRatio === 0 && endRatio === 1) { + isSelected = false; + } + // 通过扇形内径/外径的值,换算出辅助参数 k(默认值 1/3) + k = typeof k !== "undefined" ? k : 1 / 3; + // 计算选中效果分别在 x 轴、y 轴方向上的位移(未选中,则位移均为 0) + const offsetX = isSelected ? Math.cos(midRadian) * 0.1 : 0; + const offsetY = isSelected ? Math.sin(midRadian) * 0.1 : 0; + // 计算高亮效果的放大比例(未高亮,则比例为 1) + const hoverRate = isHovered ? 1.05 : 1; + // 返回曲面参数方程 + return { + u: { + min: -Math.PI, + max: Math.PI * 3, + step: Math.PI / 32 + }, + v: { + min: 0, + max: Math.PI * 2, + step: Math.PI / 20 + }, + x: function (u, v) { + if (u < startRadian) { + return ( + offsetX + Math.cos(startRadian) * (1 + Math.cos(v) * k) * hoverRate + ); + } + if (u > endRadian) { + return ( + offsetX + Math.cos(endRadian) * (1 + Math.cos(v) * k) * hoverRate + ); + } + return offsetX + Math.cos(u) * (1 + Math.cos(v) * k) * hoverRate; + }, + y: function (u, v) { + if (u < startRadian) { + return ( + offsetY + Math.sin(startRadian) * (1 + Math.cos(v) * k) * hoverRate + ); + } + if (u > endRadian) { + return ( + offsetY + Math.sin(endRadian) * (1 + Math.cos(v) * k) * hoverRate + ); + } + return offsetY + Math.sin(u) * (1 + Math.cos(v) * k) * hoverRate; + }, + z: function (u, v) { + if (u < -Math.PI * 0.5) { + return Math.sin(u); + } + if (u > Math.PI * 2.5) { + return Math.sin(u) * h * 0.1; + } + return Math.sin(v) > 0 ? 1 * h * 0.1 : -1; + } + }; +}; + +/** + * 获取3d丙图的最高扇区的高度 + */ +const getHeight3D = (series, height) => { + series.sort((a, b) => { + return b.pieData.value - a.pieData.value; + }); + return (height * 25) / series[0].pieData.value; +}; + +/** + * 格式化浮点数 + */ +const fomatFloat = (num, n) => { + let f = parseFloat(num); + if (isNaN(f)) { + return false; + } + f = Math.round(num * Math.pow(10, n)) / Math.pow(10, n); // n 幂 + let s = f.toString(); + let rs = s.indexOf("."); + // 判定如果是整数,增加小数点再补0 + if (rs < 0) { + rs = s.length; + s += "."; + } + while (s.length <= rs + n) { + s += "0"; + } + return s; +}; + +export { getPie3D, getParametricEquation }; diff --git a/src/utils/common.js b/src/utils/common.js index 13c0b0e..e82eba6 100644 --- a/src/utils/common.js +++ b/src/utils/common.js @@ -60,6 +60,75 @@ export const exceptStr = (str) => { return {}; } }; +/** + * @param {date} time 需要转换的时间 + * @param {String} fmt 需要转换的格式 如 yyyy-MM-dd、yyyy-MM-dd HH:mm:ss + */ +export const formatTime = (time, fmt) => { + if (!time) return ""; + else { + const date = new Date(time); + const o = { + "M+": date.getMonth() + 1, + "d+": date.getDate(), + "H+": date.getHours(), + "m+": date.getMinutes(), + "s+": date.getSeconds(), + "q+": Math.floor((date.getMonth() + 3) / 3), + S: date.getMilliseconds() + }; + if (/(y+)/.test(fmt)) + fmt = fmt.replace( + RegExp.$1, + (date.getFullYear() + "").substr(4 - RegExp.$1.length) + ); + for (const k in o) { + if (new RegExp("(" + k + ")").test(fmt)) { + fmt = fmt.replace( + RegExp.$1, + RegExp.$1.length === 1 + ? o[k] + : ("00" + o[k]).substr(("" + o[k]).length) + ); + } + } + return fmt; + } +}; +export const debounce = (func, wait, immediate) => { + let timeout, args, context, timestamp, result; + + const later = function () { + // 据上一次触发时间间隔 + const last = +new Date() - timestamp; + + // 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait + if (last < wait && last > 0) { + timeout = setTimeout(later, wait - last); + } else { + timeout = null; + // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用 + if (!immediate) { + result = func.apply(context, args); + if (!timeout) context = args = null; + } + } + }; + + return function (...args) { + context = this; + timestamp = +new Date(); + const callNow = immediate && !timeout; + // 如果延时不存在,重新设定延时 + if (!timeout) timeout = setTimeout(later, wait); + if (callNow) { + result = func.apply(context, args); + context = args = null; + } + + return result; + }; +}; export const paginationFun = (tableListPageObj, sizeChange, onChange) => { return { @@ -76,8 +145,8 @@ export const paginationFun = (tableListPageObj, sizeChange, onChange) => { onChange({ pageNum: page, size, - total, + total }); - }, + } }; }; diff --git a/src/utils/flexible.js b/src/utils/flexible.js new file mode 100644 index 0000000..44412fe --- /dev/null +++ b/src/utils/flexible.js @@ -0,0 +1,147 @@ +(function(win, lib) { + const doc = win.document; + const docEl = doc.documentElement; + let metaEl = doc.querySelector("meta[name=\"viewport\"]"); + const flexibleEl = doc.querySelector("meta[name=\"flexible\"]"); + let dpr = 0; + let scale = 0; + let tid; + const flexible = lib.flexible || (lib.flexible = {}); + + if (metaEl) { + console.warn("将根据已有的meta标签来设置缩放比例"); + const match = metaEl + .getAttribute("content") + // eslint-disable-next-line no-useless-escape + .match(/initial\-scale=([\d\.]+)/); + if (match) { + scale = parseFloat(match[1]); + dpr = parseInt(1 / scale); + } + } else if (flexibleEl) { + const content = flexibleEl.getAttribute("content"); + if (content) { + // eslint-disable-next-line no-useless-escape + const initialDpr = content.match(/initial\-dpr=([\d\.]+)/); + // eslint-disable-next-line no-useless-escape + const maximumDpr = content.match(/maximum\-dpr=([\d\.]+)/); + if (initialDpr) { + dpr = parseFloat(initialDpr[1]); + scale = parseFloat((1 / dpr).toFixed(2)); + } + if (maximumDpr) { + dpr = parseFloat(maximumDpr[1]); + scale = parseFloat((1 / dpr).toFixed(2)); + } + } + } + + if (!dpr && !scale) { + // eslint-disable-next-line no-unused-vars + const isAndroid = win.navigator.appVersion.match(/android/gi); + const isIPhone = win.navigator.appVersion.match(/iphone/gi); + const devicePixelRatio = win.devicePixelRatio; + if (isIPhone) { + // iOS下,对于2和3的屏,用2倍的方案,其余的用1倍方案 + if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) { + dpr = 3; + } else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)) { + dpr = 2; + } else { + dpr = 1; + } + } else { + // 其他设备下,仍旧使用1倍的方案 + dpr = 1; + } + scale = 1 / dpr; + } + + docEl.setAttribute("data-dpr", dpr); + if (!metaEl) { + metaEl = doc.createElement("meta"); + metaEl.setAttribute("name", "viewport"); + metaEl.setAttribute( + "content", + "initial-scale=" + + scale + + ", maximum-scale=" + + scale + + ", minimum-scale=" + + scale + + ", user-scalable=no" + ); + if (docEl.firstElementChild) { + docEl.firstElementChild.appendChild(metaEl); + } else { + const wrap = doc.createElement("div"); + wrap.appendChild(metaEl); + doc.write(wrap.innerHTML); + } + } + + function refreshRem() { + let width = docEl.getBoundingClientRect().width; + // 最小1366px,最大适配2560px + if (width / dpr < 1366) { + width = 1366 * dpr; + } else if (width / dpr > 2560) { + width = 2560 * dpr; + } + // 设置成24等份,设计稿时1920px的,这样1rem就是80px + const rem = width / 24; + docEl.style.fontSize = rem + "px"; + flexible.rem = win.rem = rem; + } + + win.addEventListener( + "resize", + function() { + clearTimeout(tid); + tid = setTimeout(refreshRem, 300); + }, + false + ); + win.addEventListener( + "pageshow", + function(e) { + if (e.persisted) { + clearTimeout(tid); + tid = setTimeout(refreshRem, 300); + } + }, + false + ); + + if (doc.readyState === "complete") { + doc.body.style.fontSize = 12 * dpr + "px"; + } else { + doc.addEventListener( + "DOMContentLoaded", + // eslint-disable-next-line no-unused-vars + function(e) { + doc.body.style.fontSize = 12 * dpr + "px"; + }, + false + ); + } + + refreshRem(); + + flexible.dpr = win.dpr = dpr; + flexible.refreshRem = refreshRem; + flexible.rem2px = function(d) { + let val = parseFloat(d) * this.rem; + if (typeof d === "string" && d.match(/rem$/)) { + val += "px"; + } + return val; + }; + flexible.px2rem = function(d) { + let val = parseFloat(d) / this.rem; + if (typeof d === "string" && d.match(/px$/)) { + val += "rem"; + } + return val; + }; +})(window, window["lib"] || (window["lib"] = {}));