| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- <template>
- <div>
- <a-card>
- <a-space>
- <a-button type="primary" @click="showGenerateModal">
- 生成工资
- </a-button>
- <a-button @click="loadSalaryList">刷新</a-button>
- </a-space>
- </a-card>
- <a-card class="mt-4" title="工资周期列表">
- <a-table
- :columns="columns"
- :dataSource="salaryPeriods"
- :loading="loading"
- rowKey="period"
- >
- <template #status="{ record }">
- <a-tag :color="record.status === 'locked' ? 'red' : 'green'">
- {{ record.status === 'locked' ? '已锁定' : '待锁定' }}
- </a-tag>
- </template>
- <template #action="{ record }">
- <a-space>
- <a-button size="small" @click="showDetailModal(record.period)">
- 查看明细
- </a-button>
- <a-button
- v-if="record.status === 'pending'"
- size="small"
- type="primary"
- @click="lockSalary(record.period)"
- >
- 锁定
- </a-button>
- <a-button
- v-else
- size="small"
- @click="unlockSalary(record.period)"
- >
- 解锁
- </a-button>
- <a-popconfirm
- title="确定删除此工资周期吗?"
- @confirm="deleteSalary(record.period)"
- >
- <a-button size="small" danger>删除</a-button>
- </a-popconfirm>
- </a-space>
- </template>
- </a-table>
- </a-card>
- <!-- 生成工资模态框 -->
- <a-modal
- v-model:open="generateModalVisible"
- title="生成工资"
- @ok="handleGenerate"
- :confirmLoading="generating"
- >
- <a-form layout="vertical">
- <a-form-item label="工资周期" required>
- <a-month-picker
- v-model:value="generatePeriod"
- placeholder="请选择工资周期"
- style="width: 100%"
- />
- </a-form-item>
- </a-form>
- </a-modal>
- <!-- 工资明细模态框 -->
- <a-modal
- v-model:open="detailModalVisible"
- :title="`工资明细 - ${currentPeriod}`"
- width="800px"
- :footer="null"
- >
- <a-table
- :columns="detailColumns"
- :dataSource="salaryDetails"
- :loading="detailLoading"
- rowKey="user_id"
- :pagination="false"
- >
- <template #amount="{ record }">
- ¥{{ record.total_amount.toFixed(2) }}
- </template>
- </a-table>
- </a-modal>
- </div>
- </template>
- <script setup lang="ts">
- import { ref, onMounted } from 'vue';
- import { message } from 'ant-design-vue';
- import dayjs from 'dayjs';
- import { SalaryApi, createApiClient } from '@smartcut/api-client';
- import { STORAGE_KEYS } from '@smartcut/shared-utils';
- import { getApiBaseUrl } from '@/apiConfig';
- import type { SalaryPeriod, SalaryDetailItem } from '@smartcut/types';
- const apiClient = createApiClient({
- baseURL: getApiBaseUrl(),
- tokenKey: STORAGE_KEYS.FACTORY_TOKEN
- });
- const salaryApi = new SalaryApi(apiClient);
- const salaryPeriods = ref<SalaryPeriod[]>([]);
- const loading = ref(false);
- const columns = [
- { title: '工资周期', dataIndex: 'period', key: 'period', width: 150 },
- { title: '状态', dataIndex: 'status', key: 'status', width: 120, slots: { customRender: 'status' } },
- { title: '创建时间', dataIndex: 'created_at', key: 'created_at', width: 200 },
- { title: '操作', key: 'action', width: 300, slots: { customRender: 'action' } }
- ];
- const detailColumns = [
- { title: '员工', dataIndex: 'user_name', key: 'user_name', width: 150 },
- { title: '总数量', dataIndex: 'total_quantity', key: 'total_quantity', width: 120 },
- { title: '记录数', dataIndex: 'records_count', key: 'records_count', width: 100 },
- { title: '工资金额', key: 'amount', width: 150, slots: { customRender: 'amount' } }
- ];
- const generateModalVisible = ref(false);
- const generating = ref(false);
- const generatePeriod = ref<dayjs.Dayjs | null>(null);
- const detailModalVisible = ref(false);
- const currentPeriod = ref('');
- const salaryDetails = ref<SalaryDetailItem[]>([]);
- const detailLoading = ref(false);
- async function loadSalaryList() {
- loading.value = true;
- try {
- salaryPeriods.value = await salaryApi.getSalaryList();
- } catch (error) {
- message.error('加载工资列表失败');
- } finally {
- loading.value = false;
- }
- }
- function showGenerateModal() {
- generateModalVisible.value = true;
- generatePeriod.value = dayjs();
- }
- async function handleGenerate() {
- if (!generatePeriod.value) {
- message.error('请选择工资周期');
- return;
- }
- generating.value = true;
- try {
- const period = generatePeriod.value.format('YYYY-MM');
- await salaryApi.generateSalary({ period });
- message.success('工资已生成');
- generateModalVisible.value = false;
- loadSalaryList();
- } catch (error: any) {
- message.error(error.response?.data?.msg || '生成工资失败');
- } finally {
- generating.value = false;
- }
- }
- async function showDetailModal(period: string) {
- currentPeriod.value = period;
- detailModalVisible.value = true;
- detailLoading.value = true;
- try {
- salaryDetails.value = await salaryApi.getSalaryDetail(period);
- } catch (error) {
- message.error('加载工资明细失败');
- } finally {
- detailLoading.value = false;
- }
- }
- async function lockSalary(period: string) {
- try {
- await salaryApi.lockSalary(period);
- message.success('工资已锁定');
- loadSalaryList();
- } catch (error) {
- message.error('锁定失败');
- }
- }
- async function unlockSalary(period: string) {
- try {
- await salaryApi.unlockSalary(period);
- message.success('工资已解锁');
- loadSalaryList();
- } catch (error) {
- message.error('解锁失败');
- }
- }
- async function deleteSalary(period: string) {
- try {
- await salaryApi.deleteSalary(period);
- message.success('工资周期已删除');
- loadSalaryList();
- } catch (error) {
- message.error('删除失败');
- }
- }
- onMounted(() => {
- loadSalaryList();
- });
- </script>
|