Browse Source

Merge pull request 'develop' (#2) from develop into master

Reviewed-on: http://101.201.226.163:3000/TALL/check-work/pulls/2
pull/8/head^2
wally 4 years ago
parent
commit
23a74d07d6
  1. 187
      .drone.yml
  2. 6
      .env.production
  3. 4
      package.json
  4. 16
      src/App.vue
  5. 50
      src/components/BarChart/BarChart.vue
  6. 18
      src/components/LineChart/LineChart.vue
  7. 236
      src/components/List/List.vue
  8. 6
      src/config/api.js
  9. 18
      src/router/index.js
  10. 333
      src/views/Salary/Salary.vue
  11. 97
      src/views/SalarySummary/SalarySummary.vue
  12. 99
      src/views/StaffInfo/StaffInfo.vue

187
.drone.yml

@ -0,0 +1,187 @@
---
kind: pipeline
type: docker
name: development
# 挂载的主机卷,可以映射到docker容器中
volumes:
# maven构建缓存(宿主机目录)
- name: ssh_key
host:
path: /root/.ssh/
- name: cache
host:
path: /var/lib/cache
- name: data
host:
path: /var/lib/data
steps:
# - name: restore-cache
# image: drillster/drone-volume-cache
# volumes:
# - name: cache
# path: /cache
# settings:
# restore: true
# mount:
# - ./node_modules
- name: build
image: node:16
pull: if-not-exists # default always
# volumes:
# - name: cache
# path: /root/.m2
commands:
- npm config set registry http://registry.npm.taobao.org
- npm i
- npm run build:test
# - name: rebuild-cache
# image: drillster/drone-volume-cache
# volumes:
# - name: cache
# path: /cache
# settings:
# rebuild: true
# mount:
# - ./node_modules
- name: deploy-scp
image: appleboy/drone-scp
pull: if-not-exists
volumes:
- name: ssh_key
path: /root/.ssh/
settings:
host: test.tall.wiki
port: 22
username: root
key_path: /root/.ssh/id_rsa
rm: true # true则会删除目标目录重建
target: /home/checkwork
source: dist/*
strip_components: 1 # 去除的目录层数,如果没有该选项,则拷贝过去是 target/xxx.jar,1代表去除target
- name: notify-wechatwork
image: fifsky/drone-wechat-work
pull: if-not-exists
settings:
url: https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=428e5c5d-f992-4349-939d-9c99556e50b8
msgtype: markdown
content: |
{{if eq .Status "success" }}
#### 🎉 ${DRONE_REPO} 构建成功
> Commit: [${DRONE_COMMIT_MESSAGE}](${DRONE_COMMIT_LINK})
> Author: ${DRONE_COMMIT_AUTHOR}
> [点击查看](${DRONE_BUILD_LINK})
{{else}}
#### ❌ ${DRONE_REPO} 构建失败
> Commit: [${DRONE_COMMIT_MESSAGE}](${DRONE_COMMIT_LINK})
> Author: ${DRONE_COMMIT_AUTHOR}
> 请立即修复!!!
> [点击查看](${DRONE_BUILD_LINK})
{{end}}
when:
status:
- failure
- success
trigger:
branch:
- develop
---
kind: pipeline
type: docker
name: production
# 挂载的主机卷,可以映射到docker容器中
volumes:
# maven构建缓存(宿主机目录)
- name: ssh_key
host:
path: /root/.ssh/
- name: cache
host:
path: /var/lib/cache
- name: data
host:
path: /var/lib/data
steps:
# - name: restore-cache
# image: drillster/drone-volume-cache
# volumes:
# - name: cache
# path: /cache
# settings:
# restore: true
# mount:
# - ./node_modules
- name: build
image: node
pull: if-not-exists # default always
# volumes:
# - name: cache
# path: /root/.m2
commands:
- npm config set registry http://registry.npm.taobao.org
- npm i
- npm run test
# - name: rebuild-cache
# image: drillster/drone-volume-cache
# volumes:
# - name: cache
# path: /cache
# settings:
# rebuild: true
# mount:
# - ./node_modules
- name: deploy-scp
image: appleboy/drone-scp
pull: if-not-exists
volumes:
- name: ssh_key
path: /root/.ssh/
settings:
host: www.tall.wiki
port: 22
username: root
key_path: /root/.ssh/id_rsa
rm: true # true则会删除目标目录重建
target: /home/checkwork
source: dist/*
strip_components: 1 # 去除的目录层数,如果没有该选项,则拷贝过去是 target/xxx.jar,1代表去除target
- name: notify-wechatwork
image: fifsky/drone-wechat-work
pull: if-not-exists
settings:
url: https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=428e5c5d-f992-4349-939d-9c99556e50b8
msgtype: markdown
content: |
{{if eq .Status "success" }}
#### 🎉 ${DRONE_REPO} 构建成功
> Commit: [${DRONE_COMMIT_MESSAGE}](${DRONE_COMMIT_LINK})
> Author: ${DRONE_COMMIT_AUTHOR}
> [点击查看](${DRONE_BUILD_LINK})
{{else}}
#### ❌ ${DRONE_REPO} 构建失败
> Commit: [${DRONE_COMMIT_MESSAGE}](${DRONE_COMMIT_LINK})
> Author: ${DRONE_COMMIT_AUTHOR}
> 请立即修复!!!
> [点击查看](${DRONE_BUILD_LINK})
{{end}}
when:
status:
- failure
- success
trigger:
branch:
- master

6
.env.production

@ -1,10 +1,10 @@
VUE_APP_MODE=production
VUE_APP_NODE_ENV=production
VUE_APP_SCENE=checkwork
VUE_APP_BASE_URL=https://test.tall.wiki/checkwork/
VUE_APP_API_URL=https://test.tall.wiki/checkwork/gateway
VUE_APP_BASE_URL=https://www.tall.wiki/checkwork/
VUE_APP_API_URL=https://www.tall.wiki/checkwork/gateway
VUE_APP_PROXY_URL=/gateway
VUE_APP_PUBLIC_PATH=/checkwork
VUE_APP_MSG_URL=wss://test.tall.wiki/websocket/message/v4.0/ws
VUE_APP_MSG_URL=wss://www.tall.wiki/websocket/message/v4.0/ws
VUE_APP_TITLE=考勤管理
VUE_APP_DESCRIPTION=考勤管理

4
package.json

@ -3,8 +3,10 @@
"version": "0.1.0",
"private": true,
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"build:test": "vue-cli-service build --mode development",
"build": "vue-cli-service build --mode production",
"lint": "vue-cli-service lint"
},
"dependencies": {

16
src/App.vue

@ -17,13 +17,15 @@ export default {
},
async created() {
const userId = this.$route.query.uid;
const roleId = this.$route.query.rid;
const params = { userId };
await this.getUserId(params);
await this.setRoleId(roleId);
await this.getAllMembers({ projectId: this.$route.query.pid });
this.setProjectId(this.$route.query.pid);
this.$router.onReady(async () => {
const userId = this.$route.query.uid;
const roleId = this.$route.query.rid;
const params = { userId };
await this.getUserId(params);
await this.setRoleId(roleId);
await this.getAllMembers({ projectId: this.$route.query.pid });
this.setProjectId(this.$route.query.pid);
});
},
methods: {

50
src/components/BarChart/BarChart.vue

@ -1,5 +1,5 @@
<template>
<div id="linBarbox" :style="{ width: '100%', height: '200px' }" class="chart-box">
<div id="linBarbox" :style="{ width: '100%', height: '300px' }" class="chart-box">
<div :id="id" style="width: 100%; height: 100%"></div>
</div>
</template>
@ -34,10 +34,9 @@ export default {
{
type: 'value',
splitLine: { show: false },
// boundaryGap: false,
// show: true,
// axisLine: { show: false },
// axisTick: { show: false },
min: 0,
max: 400,
splitNumber: 5,
},
],
yAxis: [
@ -47,20 +46,19 @@ export default {
data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
axisLine: { show: false },
axisTick: { show: false },
// splitLine: { show: false },
// data: ['0', '10', '20'],
},
// {
// show: false,
// type: 'value',
// },
],
series: [
{
type: 'bar',
// yAxisIndex: 1,
color: `${this.color[0]}`,
barWidth: '10',
barWidth: '20',
label: {
show: true, //
position: 'insideRight',
color: '#fff',
fontSize: 12,
},
data: [
{
value: 200,
@ -92,32 +90,6 @@ export default {
},
],
},
// {
// type: 'line',
// stack: '',
// smooth: true,
// lineStyle: {
// width: 2,
// color: `rgba(105,207,76,1)`,
// boxShadow: '10px 10px 10px #000',
// },
// showSymbol: false,
// areaStyle: {
// opacity: 0.8,
// color: new this.$echarts.graphic.LinearGradient(0, 0, 0, 1, [
// {
// offset: 0,
// color: `rgba(105,207,76,0.4)`,
// },
// {
// offset: 1,
// color: `rgba(105,207,76,0)`,
// },
// ]),
// },
// emphasis: { focus: 'series' },
// data: [140, 232, 101, 264, 90, 340, 250],
// },
],
};
},

18
src/components/LineChart/LineChart.vue

@ -33,8 +33,6 @@ export default {
xAxis: [
{
type: 'category',
// boundaryGap: false,
// show: true,
axisLine: { show: false },
axisTick: { show: false },
data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
@ -42,24 +40,14 @@ export default {
],
yAxis: [
{
// show: true,
type: 'value',
splitLine: { show: false },
data: ['0', '10', '20'],
min: 0,
max: 400,
splitNumber: 5,
},
// {
// show: false,
// type: 'value',
// },
],
series: [
// {
// type: 'bar',
// yAxisIndex: 1,
// color: `rgba(105,207,76,0.4)`,
// barWidth: '10',
// data: [null, 140, 232, 101, 264, 90, 340, 250, null],
// },
{
type: 'line',
stack: '总量',

236
src/components/List/List.vue

@ -33,12 +33,11 @@
<span v-else-if="record.morningStatus === 3" class="text-blue-500 font-bold">
{{ $moment(record.morning - 0).format('HH:mm') }}
</span>
<span v-else-if="today !== list.dateTime">未打卡</span>
<span v-else-if="today < list.dateTime">未打卡</span>
<a-button
v-else
type="primary"
size="small"
:loading="morningLoading"
@click="checkTime(listIndex, index, 0, record.id, record.memberId, record.checkerId, list.dateTime)"
>
打卡
@ -92,7 +91,7 @@
format="HH:mm"
class="px-2"
@change="timeChange"
@openChange="openChange($event, record, 'showMorningTime', 'morning')"
@openChange="openChange($event, record, 1, 'showMorningTime', 'morning')"
/>
</div>
</div>
@ -119,12 +118,11 @@
<span v-else-if="record.nightStatus === 3" class="text-blue-500 font-bold">
{{ $moment(record.night - 0).format('HH:mm') }}
</span>
<span v-else-if="today !== list.dateTime">未打卡</span>
<span v-else-if="today < list.dateTime">未打卡</span>
<a-button
v-else
type="primary"
size="small"
:loading="nightLoading"
@click="checkTime(listIndex, index, 1, record.id, record.memberId, record.checkerId, list.dateTime)"
>
打卡
@ -178,7 +176,7 @@
format="HH:mm"
class="px-2"
@change="timeChange"
@openChange="openChange($event, record, 'showNightTime', 'night')"
@openChange="openChange($event, record, 1, 'showNightTime', 'night')"
/>
</div>
</div>
@ -186,7 +184,7 @@
<!-- 审核人 -->
<template slot="checkerName" slot-scope="text, record">
<div class="px-2">
<div v-if="!record.isMine || (record.isMine && record.morningStatus && record.nightStatus) || today !== list.dateTime">
<div v-if="!record.isMine || (record.isMine && record.morningStatus && record.nightStatus)">
{{ record.checkerName || checkers[0].name }}
</div>
<a-select
@ -203,6 +201,47 @@
</template>
</a-table>
</div>
<a-modal :visible="visible" :centered="true" :closable="false" @ok="handleOk" @cancel="handleCancel">
<div class="modal-title">打卡</div>
<div class="modal-con flex justify-between items-center">
<div class="mr-3 flex-shrink-0">打卡原因</div>
<a-input class="text-right focus:outline-none" type="text" v-model="remark" placeholder="请输入打卡原因" />
</div>
<div class="modal-con flex justify-between items-center">
<div class="mr-3 flex-shrink-0">打卡时间</div>
<!-- 修改时间 -->
<a-time-picker
placeholder="请选择"
format="HH:mm"
:default-value="defaultValue"
@change="timeChange"
@openChange="
openChange(
$event,
recordInfo,
0,
punchTimeType === 0 ? 'showMorningTime' : 'showNightTime',
punchTimeType === 0 ? 'morning' : 'night',
)
"
/>
</div>
<div class="modal-con flex justify-between items-center">
<div class="mr-3 flex-shrink-0">审核人</div>
<a-select
style="width: 100px"
:default-value="
recordInfo.lastCheckerId ? recordInfo.lastCheckerId : recordInfo.checkerId ? recordInfo.checkerId : checkers[0].memberId
"
@change="chooseChecker"
>
<a-select-option :value="member.memberId" v-for="member in checkers" :key="member.memberId">
{{ member.name }}
</a-select-option>
</a-select>
</div>
</a-modal>
</div>
<a-empty class="mt-8 mb-8" description="暂无打卡信息" v-else />
</div>
@ -238,10 +277,16 @@ export default {
timer: null,
chooseTime: '',
auditOptions: null,
morningLoading: false,
nightLoading: false,
// morningLoading: false,
// nightLoading: false,
today: this.$moment(new Date()).format('YYYY-MM-DD'),
selectedDate: '', //
visible: false,
punchParams: {}, //
recordInfo: {}, //
punchTimeType: 0, // 0 1
remark: '', //
defaultValue: this.$moment(new Date()), //
};
},
@ -253,6 +298,7 @@ export default {
clearInterval(this.timer);
await this.setParams();
console.log(this.defaultValue);
//
// document.querySelector('#scrollTo').scrollIntoView({
// behavior: 'smooth', //
@ -279,6 +325,18 @@ export default {
await this.getClockQuery(params);
},
showModal() {
this.visible = true;
},
handleOk(e) {
this.handleClockPunch(this.punchParams);
},
handleCancel(e) {
this.visible = false;
},
/**
* 查询考勤信息
* @param {string} projectId
@ -317,6 +375,9 @@ export default {
//
checkTime(listIndex, index, clockType, id, memberId, checkerId, clockTime) {
this.auditOptions = { id, clockType };
this.showModal();
const time = Date.now();
const selectTime = this.$moment(time).format('HH:mm');
if (clockType === 0) {
@ -324,9 +385,13 @@ export default {
} else {
this.clockInfos[listIndex].recordList[index].night = selectTime;
}
this.recordInfo = this.clockInfos[listIndex].recordList[index];
this.punchTimeType = clockType;
this.selectedDate = clockTime;
const dateTime = this.$moment(`${clockTime} ${selectTime}`).format('x') - 0;
const params = { param: { checkerId: this.checkerId || checkerId || this.checkers[0].memberId, clockType, dateTime, id, memberId } };
this.handleClockPunch(params);
this.punchParams = params;
// this.handleClockPunch(params);
},
/**
@ -339,11 +404,27 @@ export default {
*/
async handleClockPunch(params) {
try {
if (params.param.clockType === 0) {
this.morningLoading = true;
} else {
this.nightLoading = true;
// if (params.param.clockType === 0) {
// this.morningLoading = true;
// } else {
// this.nightLoading = true;
// }
if (this.checkerId) {
params.param.checkerId = this.checkerId;
}
if (this.selectedDate != this.today) {
if (!this.remark) {
this.$message.error('请填写打卡原因');
return false;
}
}
if (this.remark) {
params.param.remark = this.remark;
}
const res = await clockPunch(params);
const { code, msg } = res.data;
if (code === 200) {
@ -354,17 +435,21 @@ export default {
this.$message.error(msg || '打卡失败');
throw msg;
}
if (params.param.clockType === 0) {
this.morningLoading = false;
} else {
this.nightLoading = false;
}
// if (params.param.clockType === 0) {
// this.morningLoading = false;
// } else {
// this.nightLoading = false;
// }
setTimeout(() => {
this.visible = false;
}, 2000);
} catch (error) {
if (params.param.clockType === 0) {
this.morningLoading = false;
} else {
this.nightLoading = false;
}
// if (params.param.clockType === 0) {
// this.morningLoading = false;
// } else {
// this.nightLoading = false;
// }
throw error || '打卡失败';
}
},
@ -395,14 +480,36 @@ export default {
this.selectedDate = selectedDate;
this.auditOptions = { id, type };
},
//
timeChange(time) {
let updateTime = this.$moment(time).format('HH:mm:ss');
let updateDate = this.selectedDate + ' ' + updateTime;
this.chooseTime = this.$moment(updateDate).format('x');
this.chooseTime = this.$moment(updateDate).format('x') - 0;
},
async openChange(open, record, show, timeType) {
if (!open && this.chooseTime) {
async openChange(open, record, source, show, timeType) {
if (source === 0) {
let clockType = 0;
if (timeType === 'morning') {
clockType = 0;
} else {
clockType = 1;
}
const params = {
param: {
checkerId: this.checkerId || record.checkerId || this.checkers[0].memberId,
clockType,
dateTime: this.chooseTime,
id: record.id,
memberId: record.memberId,
},
};
this.punchParams = params;
}
if (!open && this.chooseTime && source === 1) {
this.auditOptions[timeType] = this.chooseTime;
this.auditOptions.projectId = this.projectId;
const params = { param: this.auditOptions };
@ -531,6 +638,81 @@ img {
background-color: #10b981 !important;
color: #fff;
}
>>> .ant-modal-body {
padding: 16px;
}
.modal-title {
font-size: 17px;
font-weight: bold;
height: 56px;
line-height: 56px;
text-align: center;
}
.modal-con {
height: 48px;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}
.modal-con .ant-input,
.modal-con .ant-input:hover,
.modal-con .ant-input:focus,
.modal-con >>> .ant-time-picker-input {
padding: 0;
border: none;
box-shadow: none;
}
.modal-con >>> .ant-time-picker {
width: 130px;
}
.modal-con >>> .ant-time-picker-input {
text-align: right;
padding-right: 20px;
}
.modal-con >>> .ant-time-picker-icon,
.modal-con >>> .ant-time-picker-clear,
.modal-con >>> .ant-select-arrow {
right: 0;
}
.modal-con >>> .ant-select-selection,
.modal-con >>> .ant-select-selection:hover {
border: none;
}
.modal-con >>> .ant-select-selection-selected-value {
float: unset;
text-align: right;
}
>>> .ant-modal-footer {
margin-top: 16px;
padding: 0;
}
>>> .ant-modal-footer div {
display: flex;
}
>>> .ant-modal-footer div .ant-btn {
width: 50%;
height: 56px;
margin: 0;
border: 0;
border-radius: 0;
font-size: 17px;
}
>>> .ant-modal-footer div .ant-btn.ant-btn-primary {
background: transparent;
color: #1890ff;
border-left: 1px solid #e8e8e8;
}
</style>
<style>

6
src/config/api.js

@ -16,3 +16,9 @@ export const clockAudit = params => axios.post(`${defaultwbs}/clock/audit`, para
// 导出考勤excel
export const clockExport = params => axios.post(`${defaultwbs}/clock/export`, params);
// 查询所有成员工资信息
export const queryMemberSalary = params => axios.post(`${defaultwbs}/salary/queryMemberSalary`, params);
// 查询某个员工工资
export const getMemberSalary = params => axios.post(`${defaultwbs}/salary/getMemberSalary`, params);

18
src/router/index.js

@ -10,25 +10,25 @@ const routes = [
path: '/',
name: 'Home',
component: Home,
meta: {
title: '考勤',
},
meta: { title: '考勤' },
},
{
path: '/salary',
name: 'salary',
component: () => import('views/Salary/Salary.vue'),
meta: {
title: '工资表',
},
meta: { title: '工资表' },
},
{
path: '/salary-summary',
name: 'salary-summary',
component: () => import('views/SalarySummary/SalarySummary.vue'),
meta: {
title: '工资汇总',
},
meta: { title: '工资汇总' },
},
{
path: '/staff-info',
name: 'staff-info',
component: () => import('views/StaffInfo/StaffInfo.vue'),
meta: { title: '员工信息完善' },
},
];

333
src/views/Salary/Salary.vue

@ -1,7 +1,7 @@
<template>
<div class="salary-detail">
<div class="salary-all text-center flex flex-col justify-center">
<div class="salary-num white--text">5000.00</div>
<div class="salary-num white--text">{{ Number(!salaryData.realSalary ? 0 : salaryData.realSalary).toFixed(2) }}</div>
<div class="salary-title font-12">实发金额</div>
</div>
@ -9,64 +9,176 @@
<div class="one-level mt-3 px-4 white">
<div class="one-level-box flex justify-between items-center h-48 border-b">
<div>固定工资</div>
<div class="flex items-center">
<span>5000</span>
<!-- <a-icon class="icon" type="right" /> -->
<a-icon class="icon" type="down" />
<div class="flex items-center" @click="isShow.isShow1 = isShow.isShow1 ? false : true">
<span>{{ !salaryData.fixedSalary ? 0 : salaryData.fixedSalary }}</span>
<a-icon v-if="!isShow.isShow1" class="icon" type="right" />
<a-icon v-else class="icon" type="down" />
</div>
</div>
<div class="two-level">
<div class="two-level-box flex justify-between items-center h-48 border-b">
<div>基本工资</div>
<div class="flex items-center">
<span>4000</span>
<!-- <a-icon class="icon" type="right" /> -->
<a-icon class="icon" type="down" />
<template v-if="isShow.isShow1">
<div class="two-level">
<div class="two-level-box flex justify-between items-center h-48 border-b">
<div>基本工资</div>
<div class="flex items-center" @click.stop="basicShow.isShow1 = basicShow.isShow1 ? false : true">
<span>{{ !fixedSalary.basicWage ? 0 : fixedSalary.basicWage }}</span>
<a-icon v-if="!basicShow.isShow1" class="icon" type="right" />
<a-icon v-else class="icon" type="down" />
</div>
</div>
<div class="three-level px-4" v-if="basicShow.isShow1">
<div class="three-level-box flex justify-between items-center h-48">
<div>应出勤天数</div>
<div class="flex items-center">
<span>{{ !basicSalary.shouldAttendanceDay ? 0 : basicSalary.shouldAttendanceDay }}</span>
</div>
</div>
<div class="three-level-box flex justify-between items-center h-48">
<div>实际出勤天数</div>
<div class="flex items-center">
<span>{{ !basicSalary.realAttendanceDay ? 0 : basicSalary.realAttendanceDay }}</span>
</div>
</div>
<div class="three-level-box flex justify-between items-center h-48">
<div>弹性天数</div>
<div class="flex items-center">
<span>{{ !basicSalary.elasticityDay ? 0 : basicSalary.elasticityDay }}</span>
</div>
</div>
<div class="three-level-box flex justify-between items-center h-48">
<div>请假天数</div>
<div class="flex items-center">
<span>{{ !basicSalary.askForLeaveDay ? 0 : basicSalary.askForLeaveDay }}</span>
</div>
</div>
</div>
</div>
<div class="two-level">
<div class="two-level-box flex justify-between items-center h-48 border-b">
<div>岗位工资</div>
<div class="flex items-center" @click.stop="basicShow.isShow2 = basicShow.isShow2 ? false : true">
<span>{{ !fixedSalary.positionWage ? 0 : fixedSalary.positionWage }}</span>
<a-icon v-if="!basicShow.isShow2" class="icon" type="right" />
<a-icon v-else class="icon" type="down" />
</div>
</div>
<div class="three-level px-4" v-if="basicShow.isShow2 && positionSalary">
<div class="three-level-box flex justify-between items-center h-48" v-for="(val, key) in positionSalary" :key="key">
<div>{{ key }}</div>
<div class="flex items-center">
<span>{{ val }}</span>
</div>
</div>
</div>
</div>
<div class="two-level">
<div class="two-level-box flex justify-between items-center h-48 border-b">
<div>工龄工资</div>
<div class="flex items-center" @click.stop="basicShow.isShow3 = basicShow.isShow3 ? false : true">
<span>{{ !fixedSalary.workingTimeWage ? 0 : fixedSalary.workingTimeWage }}</span>
<a-icon v-if="!basicShow.isShow3" class="icon" type="right" />
<a-icon v-else class="icon" type="down" />
</div>
</div>
<div class="three-level px-4" v-if="basicShow.isShow3 && workingTimeSalary">
<div class="three-level-box flex justify-between items-center h-48" v-for="(val, key) in workingTimeSalary" :key="key">
<div>{{ key }}</div>
<div class="flex items-center">
<span>{{ val }}</span>
</div>
</div>
</div>
</div>
</template>
</div>
<div class="one-level mt-3 px-4 white" @click.stop="isShow.isShow2 = isShow.isShow2 ? false : true">
<div class="one-level-box flex justify-between items-center h-48 border-b">
<div>绩效收入</div>
<div class="flex items-center">
<span>{{ !salaryData.performance ? 0 : salaryData.performance }}</span>
<a-icon v-if="!isShow.isShow2" class="icon" type="right" />
<a-icon v-else class="icon" type="down" />
</div>
</div>
<div class="three-level px-4">
<div class="three-level-box flex justify-between items-center h-48">
<div>应出勤天数</div>
<div class="flex items-center"><span>22</span></div>
<template v-if="isShow.isShow2">
<div class="two-level">
<div class="two-level-box flex justify-between items-center h-48 border-b">
<div>360考核</div>
<div class="flex items-center" @click.stop="performanceShow.isShow1 = performanceShow.isShow1 ? false : true">
<span>{{ !performanceSalary.assess ? 0 : performanceSalary.assess }}</span>
<a-icon v-if="!performanceShow.isShow1" class="icon" type="right" />
<a-icon v-else class="icon" type="down" />
</div>
</div>
</div>
<div class="three-level-box flex justify-between items-center h-48">
<div>实际出勤天数</div>
<div class="flex items-center"><span>18</span></div>
<div class="two-level">
<div class="two-level-box flex justify-between items-center h-48 border-b">
<div>项目奖金</div>
<div class="flex items-center" @click.stop="performanceShow.isShow2 = performanceShow.isShow2 ? false : true">
<span>{{ !performanceSalary.projectBonus ? 0 : performanceSalary.projectBonus }}</span>
<a-icon v-if="!performanceShow.isShow2" class="icon" type="right" />
<a-icon v-else class="icon" type="down" />
</div>
</div>
</div>
<div class="three-level-box flex justify-between items-center h-48">
<div>弹性天数</div>
<div class="flex items-center"><span>2</span></div>
<div class="two-level">
<div class="two-level-box flex justify-between items-center h-48 border-b">
<div>提成</div>
<div class="flex items-center" @click.stop="performanceShow.isShow3 = performanceShow.isShow3 ? false : true">
<span>{{ !performanceSalary.pushMoney ? 0 : performanceSalary.pushMoney }}</span>
<a-icon v-if="!performanceShow.isShow3" class="icon" type="right" />
<a-icon v-else class="icon" type="down" />
</div>
</div>
</div>
<div class="three-level-box flex justify-between items-center h-48">
<div>请假天数</div>
<div class="flex items-center"><span>2</span></div>
<div class="two-level">
<div class="two-level-box flex justify-between items-center h-48 border-b">
<div>补助</div>
<div class="flex items-center" @click.stop="performanceShow.isShow4 = performanceShow.isShow4 ? false : true">
<span>{{ !performanceSalary.subsidy ? 0 : performanceSalary.subsidy }}</span>
<a-icon v-if="!performanceShow.isShow4" class="icon" type="right" />
<a-icon v-else class="icon" type="down" />
</div>
</div>
</div>
</template>
</div>
<div class="one-level mt-3 px-4 white">
<div class="one-level-box flex justify-between items-center h-48 border-b">
<div>主人翁收入</div>
<div class="flex items-center" @click="isShow.isShow3 = isShow.isShow3 ? false : true">
<span>{{ !salaryData.stockRights ? 0 : salaryData.stockRights }}</span>
<a-icon v-if="!isShow.isShow3" class="icon" type="right" />
<a-icon v-else class="icon" type="down" />
</div>
</div>
<div class="two-level">
<div class="two-level" v-if="isShow.isShow3">
<div class="two-level-box flex justify-between items-center h-48 border-b">
<div>岗位工资</div>
<div>期权置换</div>
<div class="flex items-center">
<span>500</span>
<a-icon class="icon" type="right" />
<!-- <a-icon class="icon" type="down" /> -->
<span>{{ !stockRightsSalary.sharePtion ? 0 : stockRightsSalary.sharePtion }}</span>
</div>
</div>
</div>
<div class="two-level">
<div class="two-level-box flex justify-between items-center h-48 border-b">
<div>工龄工资</div>
<div>分红</div>
<div class="flex items-center">
<span>500</span>
<a-icon class="icon" type="right" />
<!-- <a-icon class="icon" type="down" /> -->
<span>{{ !stockRightsSalary.receiveDividends ? 0 : stockRightsSalary.receiveDividends }}</span>
</div>
</div>
</div>
@ -74,65 +186,63 @@
<div class="one-level mt-3 px-4 white">
<div class="one-level-box flex justify-between items-center h-48 border-b">
<div>固定工资</div>
<div>应发工资</div>
<div class="flex items-center">
<span>5000</span>
<!-- <a-icon class="icon" type="right" /> -->
<a-icon class="icon" type="down" />
<span>{{ !salaryData.shouldSalary ? 0 : salaryData.shouldSalary }}</span>
</div>
</div>
</div>
<div class="one-level mt-3 px-4 white">
<div class="one-level-box flex justify-between items-center h-48 border-b">
<div>社保支出</div>
<div class="flex items-center" @click="isShow.isShow4 = isShow.isShow4 ? false : true">
<span>{{ !salaryData.socialSecurity ? 0 : salaryData.socialSecurity }}</span>
<a-icon v-if="!isShow.isShow4" class="icon" type="right" />
<a-icon v-else class="icon" type="down" />
</div>
</div>
<div class="two-level">
<div class="two-level" v-if="isShow.isShow4">
<div class="two-level-box flex justify-between items-center h-48 border-b">
<div>基本工资</div>
<div>医疗保险</div>
<div class="flex items-center">
<span>4000</span>
<!-- <a-icon class="icon" type="right" /> -->
<a-icon class="icon" type="down" />
<span>{{ !socialSecuritySalary.medicalInsurance ? 0 : socialSecuritySalary.medicalInsurance }}</span>
</div>
</div>
<div class="three-level px-4">
<div class="three-level-box flex justify-between items-center h-48">
<div>应出勤天数</div>
<div class="flex items-center"><span>22</span></div>
</div>
<div class="three-level-box flex justify-between items-center h-48">
<div>实际出勤天数</div>
<div class="flex items-center"><span>18</span></div>
<div class="two-level-box flex justify-between items-center h-48 border-b">
<div>生育保险</div>
<div class="flex items-center">
<span>{{ !socialSecuritySalary.maternityInsurance ? 0 : socialSecuritySalary.maternityInsurance }}</span>
</div>
</div>
<div class="three-level-box flex justify-between items-center h-48">
<div>弹性天数</div>
<div class="flex items-center"><span>2</span></div>
<div class="two-level-box flex justify-between items-center h-48 border-b">
<div>工伤保险</div>
<div class="flex items-center">
<span>{{ !socialSecuritySalary.employmentInjuryInsurance ? 0 : socialSecuritySalary.employmentInjuryInsurance }}</span>
</div>
</div>
<div class="three-level-box flex justify-between items-center h-48">
<div>请假天数</div>
<div class="flex items-center"><span>2</span></div>
<div class="two-level-box flex justify-between items-center h-48 border-b">
<div>养老保险</div>
<div class="flex items-center">
<span>{{ !socialSecuritySalary.endowmentInsurance ? 0 : socialSecuritySalary.endowmentInsurance }}</span>
</div>
</div>
</div>
<div class="two-level">
<div class="two-level-box flex justify-between items-center h-48 border-b">
<div>岗位工资</div>
<div>失业保险</div>
<div class="flex items-center">
<span>500</span>
<a-icon class="icon" type="right" />
<!-- <a-icon class="icon" type="down" /> -->
<span>{{ !socialSecuritySalary.unemploymentInsurance ? 0 : socialSecuritySalary.unemploymentInsurance }}</span>
</div>
</div>
</div>
<div class="two-level">
<div class="two-level-box flex justify-between items-center h-48 border-b">
<div>工龄工资</div>
<div>住房公积金</div>
<div class="flex items-center">
<span>500</span>
<a-icon class="icon" type="right" />
<!-- <a-icon class="icon" type="down" /> -->
<span>{{ !socialSecuritySalary.housingFund ? 0 : socialSecuritySalary.housingFund }}</span>
</div>
</div>
</div>
@ -140,11 +250,18 @@
<div class="one-level mt-3 px-4 white">
<div class="one-level-box flex justify-between items-center h-48 border-b">
<div>绩效收入</div>
<div>扣税</div>
<div class="flex items-center">
<span>{{ !salaryData.personalTax ? 0 : salaryData.personalTax }}</span>
</div>
</div>
</div>
<div class="one-level mt-3 px-4 white">
<div class="one-level-box flex justify-between items-center h-48 border-b">
<div>实发工资</div>
<div class="flex items-center">
<span>1000</span>
<a-icon class="icon" type="right" />
<!-- <a-icon class="icon" type="down" /> -->
<span>{{ !salaryData.realSalary ? 0 : salaryData.realSalary }}</span>
</div>
</div>
</div>
@ -162,15 +279,54 @@
</template>
<script>
import { mapState } from 'vuex';
import { getMemberSalary } from '@/config/api';
export default {
data() {
return {
ModalText: '',
visible: false,
confirmLoading: false,
salaryData: {},
fixedSalary: {}, //
basicSalary: {}, //
positionSalary: {}, //
workingTimeSalary: {}, //
performanceSalary: {}, //
socialSecuritySalary: {}, //
stockRightsSalary: {}, //
isShow: {
isShow1: false,
isShow2: false,
isShow3: false,
isShow4: false,
},
basicShow: {
isShow1: false,
isShow2: false,
isShow3: false,
},
performanceShow: {
isShow1: false,
isShow2: false,
isShow3: false,
isShow4: false,
},
};
},
computed: mapState('home', ['projectId', 'roleId']),
mounted() {
this.timer = setInterval(async () => {
if (this.projectId) {
clearInterval(this.timer);
await this.getMemberSalary();
}
}, 300);
},
methods: {
showModal() {
this.visible = true;
@ -188,6 +344,31 @@ export default {
handleCancel(e) {
this.visible = false;
},
//
async getMemberSalary() {
const params = { param: { projectId: this.projectId } };
try {
const res = await getMemberSalary(params);
const { code, msg, data } = res.data;
if (code === 200) {
this.salaryData = data;
this.fixedSalary = data.fixedSalaryOption; //
this.basicSalary = data.fixedSalaryOption.basic; // --
this.positionSalary = data.fixedSalaryOption.position; //
this.workingTimeSalary = data.fixedSalaryOption.workingTime; //
this.performanceSalary = data.performanceOption; //
this.socialSecuritySalary = data.socialSecurityOption; //
this.stockRightsSalary = data.stockRightsOption; //
} else {
this.$message.error(msg || '获取失败');
throw msg;
}
} catch (error) {
throw error || '获取失败';
}
},
},
};
</script>

97
src/views/SalarySummary/SalarySummary.vue

@ -9,7 +9,7 @@
:default-value="$moment(currDate, monthFormat)"
:format="monthFormat"
@change="onChange"
:getCalendarContainer="getCalendarContainer()"
@getCalendarContainer="getCalendarContainer()"
>
<a-icon slot="suffixIcon" type="down" />
</a-month-picker>
@ -100,8 +100,12 @@
<!-- 员工薪资排行 -->
<div class="echarts mt-3 p-4 white">
<div class="title-box flex items-center justify-between">
<div class="title-box relative flex items-center justify-between">
<div class="title">薪资top排行榜</div>
<div class="right-search absolute right-0 top-0">
<a-input-search class="search-input" placeholder="搜索" style="width: 200px" @search="onSearch" />
</div>
</div>
<div>
@ -114,6 +118,12 @@
<script>
import LineChart from '@/components/LineChart/LineChart.vue';
import BarChart from '@/components/BarChart/BarChart.vue';
import { queryMemberSalary } from '@/config/api';
import { mapState } from 'vuex';
const myDate = new Date();
const tYear = myDate.getFullYear();
const tMonth = myDate.getMonth();
const columns = [
{
@ -225,8 +235,8 @@ export default {
data() {
return {
timer: null,
monthFormat: 'YYYY年MM月',
today: this.$moment(new Date()).format('YYYY年MM月'),
isCurr: true,
columns,
data,
@ -251,14 +261,16 @@ export default {
{ key: 2, name: '项目1' },
{ key: 3, name: '项目2' },
],
monthStartTime: '',
monthEndTime: '',
dataSalary: [],
};
},
computed: {
...mapState('home', ['projectId', 'roleId']),
currDate() {
var myDate = new Date();
var tYear = myDate.getFullYear();
var tMonth = myDate.getMonth();
let m = tMonth === 0 ? 12 : tMonth;
let y = tMonth === 0 ? tYear - 1 : tYear;
let data = y + '年' + m + '月';
@ -266,13 +278,25 @@ export default {
},
},
mounted() {
this.timer = setInterval(async () => {
if (this.projectId) {
clearInterval(this.timer);
await this.getQueryMemberSalary();
}
}, 300);
this.monthStartTime = new Date(tYear, tMonth - 1, 1).getTime(); //
this.monthEndTime = new Date(tYear, tMonth, 1).getTime(); //
},
methods: {
//
onChange(date, dateString) {
if (dateString == this.today) {
isCurr = true;
if (dateString == this.currDate) {
this.isCurr = true;
} else {
isCurr = false;
this.isCurr = false;
}
},
@ -307,6 +331,42 @@ export default {
}
});
},
//
onSearch(value) {
console.log(value);
},
//
async getQueryMemberSalary() {
const params = { param: { projectId: this.projectId, startTime: this.monthStartTime, endTime: this.monthEndTime } };
try {
const res = await queryMemberSalary(params);
const { code, msg, data } = res.data;
if (code === 200) {
let dataSalary = [];
data.forEach((item, index) => {
let obj = {};
obj.key = index + 1;
obj.name = item.memberName;
obj.salary = item.realSalary;
obj.department = '技术';
obj.position = '前端';
obj.payrollCard = '111111111111111111';
dataSalary.push(obj);
});
console.log(dataSalary);
this.data_all = dataSalary;
} else {
this.$message.error(msg || '获取失败');
throw msg;
}
} catch (error) {
throw error || '获取失败';
}
},
},
};
</script>
@ -381,11 +441,22 @@ export default {
color: #595959;
}
.color-gray {
color: #929292;
.right-search {
width: 112px;
height: 24px;
.search-input {
width: 100% !important;
height: 100%;
}
/deep/ .ant-input {
padding: 0 11px;
height: 24px;
}
}
/deep/ .ant-modal {
top: calc(~'50vh - 130px');
.color-gray {
color: #929292;
}
</style>

99
src/views/StaffInfo/StaffInfo.vue

@ -0,0 +1,99 @@
<template>
<div class="staff-detail">
<div class="staff-flex white">
<div class="flex-title px-4 h-48 flex justify-between items-center border-b">个人信息</div>
<div class="flex-con px-4 h-48 flex justify-between items-center border-b">
<div>姓名</div>
<div>张三</div>
</div>
<div class="flex-con px-4 h-48 flex justify-between items-center border-b">
<div>性别</div>
<div></div>
</div>
<div class="flex-con px-4 h-48 flex justify-between items-center border-b">
<div>年龄</div>
<div>20</div>
</div>
</div>
<div class="staff-flex mt-2 white">
<div class="flex-title px-4 h-48 flex justify-between items-center border-b">岗位信息</div>
<div class="flex-con px-4 h-48 flex justify-between items-center border-b">
<div>部门</div>
<div>软件部</div>
</div>
<div class="flex-con px-4 h-48 flex justify-between items-center border-b">
<div>职务</div>
<div>项目负责人</div>
</div>
<div class="flex-con px-4 h-48 flex justify-between items-center border-b">
<div>入职时间</div>
<div>2021-10-20</div>
</div>
</div>
<div class="staff-flex mt-2 white">
<div class="flex-title px-4 h-48 flex justify-between items-center border-b">薪资信息</div>
<div class="flex-con px-4 h-48 flex justify-between items-center border-b">
<div>固定工资</div>
<div>
<a-input class="flex-input text-right" placeholder="请完善信息" type="number" v-model="fixed_salary" @change="onChange" />
</div>
</div>
<div class="flex-con px-4 h-48 flex justify-between items-center border-b">
<div>期权置换</div>
<div>
<a-input class="flex-input text-right" placeholder="请完善信息" type="number" v-model="option_salary" @change="onChange" />
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
fixed_salary: null, //
option_salary: '', //
};
},
methods: {
onChange(e) {
console.log(e);
},
},
};
</script>
<style lang="less" scoped>
.staff-detail {
width: 100%;
min-height: 100vh;
padding-bottom: 70px;
background: #f3f3f3;
}
.h-48 {
height: 48px;
}
.flex-title {
color: #595959;
font-weight: bold;
}
.flex-con {
color: #595959;
}
.flex-input {
padding: 0;
border: none;
&:hover {
box-shadow: none;
}
}
</style>
Loading…
Cancel
Save