23号BUG修复
This commit is contained in:
2
.env
2
.env
@@ -1,5 +1,5 @@
|
||||
# 网站主标题
|
||||
VITE_GLOBAL_TITLE= 'PIGX ADMIN'
|
||||
VITE_GLOBAL_TITLE= '投资管理门户'
|
||||
|
||||
# footer
|
||||
VITE_FOOTER_TITLE= '©2025 pig4cloud.com'
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<script setup lang="ts" name="StrengthMeter">
|
||||
import { verifyPasswordStrength } from '/@/utils/toolsValidate';
|
||||
const props = defineProps({
|
||||
value: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
},
|
||||
showInput: {
|
||||
@@ -28,7 +28,7 @@ const props = defineProps({
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['score', 'change', 'update:value']);
|
||||
const emit = defineEmits(['score', 'change', 'update:modelValue']);
|
||||
|
||||
// 计算密码强度
|
||||
const getPasswordStrength = computed(() => {
|
||||
@@ -47,13 +47,13 @@ const handleChange = (e: any) => {
|
||||
};
|
||||
|
||||
watchEffect(() => {
|
||||
innerValueRef.value = props.value || '';
|
||||
innerValueRef.value = props.modelValue || '';
|
||||
});
|
||||
|
||||
watch(
|
||||
() => unref(innerValueRef),
|
||||
(val) => {
|
||||
emit('update:value', val);
|
||||
emit('update:modelValue', val);
|
||||
emit('change', val);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
<el-col :span="12">
|
||||
<el-form-item :label="t('progressReport.form.completionRate')" required prop="completionRate">
|
||||
<el-input v-model="formData.completionRate"
|
||||
:placeholder="t('progressReport.form.completionRatePlaceholder')">
|
||||
:placeholder="t('progressReport.form.completionRatePlaceholder')" disabled>
|
||||
<template #append>%</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
@@ -107,13 +107,14 @@
|
||||
<div style="flex: 1;">
|
||||
<el-form-item style="height: 82px;" :label="t('progressReport.form.reportYear')">
|
||||
<el-date-picker v-model="formData.annualReport" type="year" value-format="YYYY"
|
||||
:placeholder="t('progressReport.form.selectYearPlaceholder')" style="width: 100%" />
|
||||
:placeholder="t('progressReport.form.selectYearPlaceholder')" style="width: 100%"
|
||||
@change="handleYearChange" />
|
||||
</el-form-item>
|
||||
<el-form-item style="height: 82px;"
|
||||
:label="t('progressReport.form.currentYearPlannedInvestment')" required
|
||||
prop="annualPlannedInvestment">
|
||||
<el-input v-model="formData.annualPlannedInvestment"
|
||||
:placeholder="t('progressReport.form.inputPlaceholder')">
|
||||
:placeholder="t('progressReport.form.inputPlaceholder')" disabled>
|
||||
<template #append>{{ t('progressReport.form.unitSuffix') }}</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
@@ -128,7 +129,7 @@
|
||||
<el-form-item style="height: 82px;"
|
||||
:label="t('progressReport.form.investmentCompletionRate')">
|
||||
<el-input v-model="formData.investmentCompletionRate"
|
||||
:placeholder="t('progressReport.form.inputPlaceholder')">
|
||||
:placeholder="t('progressReport.form.inputPlaceholder')" disabled>
|
||||
<template #append>%</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
@@ -141,7 +142,7 @@
|
||||
<div style="flex: 1;">
|
||||
<el-form-item style="height: 82px;" :label="t('progressReport.form.plannedPaymentAmount')">
|
||||
<el-input v-model="formData.plannedPayment"
|
||||
:placeholder="t('progressReport.form.inputPlaceholder')">
|
||||
:placeholder="t('progressReport.form.inputPlaceholder')" disabled>
|
||||
<template #append>{{ t('progressReport.form.unitSuffix') }}</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
@@ -155,7 +156,7 @@
|
||||
<el-form-item style="height: 82px;"
|
||||
:label="t('progressReport.form.plannedAmountPaymentCompletionRate')">
|
||||
<el-input v-model="formData.investmentPlanCompletionRate"
|
||||
:placeholder="t('progressReport.form.inputPlaceholder')">
|
||||
:placeholder="t('progressReport.form.inputPlaceholder')" disabled>
|
||||
<template #append>%</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
@@ -278,6 +279,10 @@ const emit = defineEmits<{
|
||||
(e: 'update:modelValue', value: InvestmentProjectProgress): void;
|
||||
}>();
|
||||
const useForm = ref<FormInstance | undefined>()
|
||||
|
||||
// 获取当前年份
|
||||
const currentYear = String(new Date().getFullYear());
|
||||
|
||||
const defaultForm = reactive<InvestmentProjectProgress>({
|
||||
id: undefined,
|
||||
projectName: '',
|
||||
@@ -290,7 +295,7 @@ const defaultForm = reactive<InvestmentProjectProgress>({
|
||||
completionRate: undefined,
|
||||
cumulativePaymentToDate: undefined,
|
||||
paymentCompletionRate: undefined,
|
||||
annualReport: undefined,
|
||||
annualReport: currentYear, // 默认当前年度
|
||||
annualPlannedInvestment: undefined,
|
||||
ytdRemainingInvestment: undefined,
|
||||
investmentCompletionRate: undefined,
|
||||
@@ -313,6 +318,12 @@ const defaultForm = reactive<InvestmentProjectProgress>({
|
||||
deptId: ''
|
||||
})
|
||||
const formData = reactive<InvestmentProjectProgress>({ ...defaultForm, ...props.modelValue });
|
||||
|
||||
// 确保汇报年度有默认值
|
||||
if (!formData.annualReport) {
|
||||
formData.annualReport = currentYear;
|
||||
}
|
||||
|
||||
const projectNameData = ref<ProjectPlanApplyFormItem[]>([]);
|
||||
// 获取当前日期作为汇报时间
|
||||
const reportTime = computed(() => {
|
||||
@@ -338,6 +349,17 @@ const getProjectNameList = async () => {
|
||||
}
|
||||
}
|
||||
getProjectNameList();
|
||||
|
||||
// 项目总计划支付额(所有年度计划支付额度的和)
|
||||
const totalPlannedPayment = ref<number>(0);
|
||||
|
||||
/**
|
||||
* 项目选择改变
|
||||
* @param {string | number} value - 选中的项目ID
|
||||
*/
|
||||
// 当前选中的项目
|
||||
const currentSelectedProject = ref<ProjectPlanApplyFormItem | null>(null);
|
||||
|
||||
/**
|
||||
* 项目选择改变
|
||||
* @param {string | number} value - 选中的项目ID
|
||||
@@ -349,11 +371,190 @@ const handelSelectChange = (value: string | number) => {
|
||||
);
|
||||
// 如果找到了对应的项目,则更新实施单位字段
|
||||
if (selectedProject) {
|
||||
currentSelectedProject.value = selectedProject;
|
||||
formData.implementingBody = selectedProject.projectMainEntity || selectedProject.projectOwnerUnit || '';
|
||||
formData.projectName = selectedProject.projectName || '';
|
||||
formData.deptId = String(selectedProject.deptId);
|
||||
|
||||
// 从项目中获取项目总投资金额
|
||||
const projectTotalAmount = selectedProject.projectTotalAmount || selectedProject.totalInvestment;
|
||||
if (projectTotalAmount) {
|
||||
formData.projectTotalAmount = Number(projectTotalAmount);
|
||||
}
|
||||
|
||||
// 计算项目总计划支付额(所有年度计划支付额度的和)
|
||||
const investmentEntities = selectedProject.projectInvestmentEntities || [];
|
||||
let totalPayment = 0;
|
||||
investmentEntities.forEach((entity: any) => {
|
||||
const paymentAmount = Number(entity.plannedPaymentAmount) || 0;
|
||||
totalPayment += paymentAmount;
|
||||
});
|
||||
totalPlannedPayment.value = totalPayment;
|
||||
|
||||
// 根据当前选中的年份更新本年度计划投资额和计划支付额
|
||||
updateAnnualPlanData(formData.annualReport as string, investmentEntities);
|
||||
|
||||
// 触发完成率计算
|
||||
calculateCompletionRates();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 年份变化处理
|
||||
* @param {string} year - 选中的年份
|
||||
*/
|
||||
const handleYearChange = (year: string) => {
|
||||
if (currentSelectedProject.value) {
|
||||
const investmentEntities = currentSelectedProject.value.projectInvestmentEntities || [];
|
||||
updateAnnualPlanData(year, investmentEntities);
|
||||
// 重新计算本年度完成率
|
||||
calculateAnnualCompletionRates();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新本年度计划投资额和计划支付额
|
||||
* @param {string} year - 年份
|
||||
* @param {any[]} investmentEntities - 投资计划实体列表
|
||||
*/
|
||||
const updateAnnualPlanData = (year: string, investmentEntities: any[]) => {
|
||||
// 查找当前年份对应的投资计划
|
||||
const currentYearPlan = investmentEntities.find((entity: any) =>
|
||||
entity.plannedInvestmentYear === year ||
|
||||
String(entity.plannedInvestmentYear) === year
|
||||
);
|
||||
|
||||
if (currentYearPlan) {
|
||||
// 设置本年度计划投资额
|
||||
formData.annualPlannedInvestment = Number(currentYearPlan.plannedImageAmount) || 0;
|
||||
// 设置本年度计划支付额
|
||||
formData.plannedPayment = Number(currentYearPlan.plannedPaymentAmount) || 0;
|
||||
} else {
|
||||
// 如果没有找到当前年份的计划,清空数据
|
||||
formData.annualPlannedInvestment = 0;
|
||||
formData.plannedPayment = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算总投资完成率和总支付完成率
|
||||
*/
|
||||
const calculateCompletionRates = () => {
|
||||
// 总投资完成率 = 项目开始截至本月末累计完成投资 / 项目总投资金额 * 100
|
||||
const cumulativeInvestment = Number(formData.cumulativeInvestmentToDate) || 0;
|
||||
const projectTotal = Number(formData.projectTotalAmount) || 0;
|
||||
|
||||
if (projectTotal > 0) {
|
||||
const rate = (cumulativeInvestment / projectTotal) * 100;
|
||||
formData.completionRate = Number(rate.toFixed(2));
|
||||
} else {
|
||||
formData.completionRate = 0;
|
||||
}
|
||||
|
||||
// 总支付完成率 = 项目开始截至本月末累计完成支付额 / 项目总计划支付额 * 100
|
||||
const cumulativePayment = Number(formData.cumulativePaymentToDate) || 0;
|
||||
const totalPayment = totalPlannedPayment.value || 0;
|
||||
|
||||
if (totalPayment > 0) {
|
||||
const rate = (cumulativePayment / totalPayment) * 100;
|
||||
formData.paymentCompletionRate = Number(rate.toFixed(2));
|
||||
} else {
|
||||
formData.paymentCompletionRate = 0;
|
||||
}
|
||||
|
||||
// 同时计算本年度完成率
|
||||
calculateAnnualCompletionRates();
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算本年度投资完成率和支付完成率
|
||||
*/
|
||||
const calculateAnnualCompletionRates = () => {
|
||||
// 本年度投资完成率 = 本企业本年度截至本月末完成投资 / 本年度计划投资额 * 100
|
||||
const ytdInvestment = Number(formData.ytdRemainingInvestment) || 0;
|
||||
const annualPlannedInvestment = Number(formData.annualPlannedInvestment) || 0;
|
||||
|
||||
if (annualPlannedInvestment > 0) {
|
||||
const rate = (ytdInvestment / annualPlannedInvestment) * 100;
|
||||
formData.investmentCompletionRate = Number(rate.toFixed(2));
|
||||
} else {
|
||||
formData.investmentCompletionRate = 0;
|
||||
}
|
||||
|
||||
// 本年度支付完成率 = 本企业本年度支付完成额 / 本年度计划支付额 * 100
|
||||
const actualPayment = Number(formData.actualPayment) || 0;
|
||||
const plannedPayment = Number(formData.plannedPayment) || 0;
|
||||
|
||||
if (plannedPayment > 0) {
|
||||
const rate = (actualPayment / plannedPayment) * 100;
|
||||
formData.investmentPlanCompletionRate = Number(rate.toFixed(2));
|
||||
} else {
|
||||
formData.investmentPlanCompletionRate = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 监听累计完成投资和项目总投资金额变化,自动计算总投资完成率
|
||||
watch(
|
||||
[() => formData.cumulativeInvestmentToDate, () => formData.projectTotalAmount],
|
||||
() => {
|
||||
const cumulativeInvestment = Number(formData.cumulativeInvestmentToDate) || 0;
|
||||
const projectTotal = Number(formData.projectTotalAmount) || 0;
|
||||
|
||||
if (projectTotal > 0) {
|
||||
const rate = (cumulativeInvestment / projectTotal) * 100;
|
||||
formData.completionRate = Number(rate.toFixed(2));
|
||||
} else {
|
||||
formData.completionRate = 0;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 监听累计完成支付额变化,自动计算总支付完成率(可编辑,仅作为默认值)
|
||||
watch(
|
||||
() => formData.cumulativePaymentToDate,
|
||||
() => {
|
||||
const cumulativePayment = Number(formData.cumulativePaymentToDate) || 0;
|
||||
const totalPayment = totalPlannedPayment.value || 0;
|
||||
|
||||
if (totalPayment > 0) {
|
||||
const rate = (cumulativePayment / totalPayment) * 100;
|
||||
formData.paymentCompletionRate = Number(rate.toFixed(2));
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 监听本年度完成投资额变化,自动计算本年度投资完成率
|
||||
watch(
|
||||
() => formData.ytdRemainingInvestment,
|
||||
() => {
|
||||
const ytdInvestment = Number(formData.ytdRemainingInvestment) || 0;
|
||||
const annualPlannedInvestment = Number(formData.annualPlannedInvestment) || 0;
|
||||
|
||||
if (annualPlannedInvestment > 0) {
|
||||
const rate = (ytdInvestment / annualPlannedInvestment) * 100;
|
||||
formData.investmentCompletionRate = Number(rate.toFixed(2));
|
||||
} else {
|
||||
formData.investmentCompletionRate = 0;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 监听本年度完成支付额变化,自动计算本年度支付完成率
|
||||
watch(
|
||||
() => formData.actualPayment,
|
||||
() => {
|
||||
const actualPayment = Number(formData.actualPayment) || 0;
|
||||
const plannedPayment = Number(formData.plannedPayment) || 0;
|
||||
|
||||
if (plannedPayment > 0) {
|
||||
const rate = (actualPayment / plannedPayment) * 100;
|
||||
formData.investmentPlanCompletionRate = Number(rate.toFixed(2));
|
||||
} else {
|
||||
formData.investmentPlanCompletionRate = 0;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* 页码大小改变
|
||||
* @param {number} pageSize - 新的页码大小
|
||||
@@ -389,6 +590,10 @@ watch(
|
||||
if (typeof newVal.annualReport === 'number') newVal.annualReport = newVal.annualReport.toString()
|
||||
Object.assign(formData, defaultForm, newVal);
|
||||
}
|
||||
// 确保汇报年度有默认值
|
||||
if (!formData.annualReport) {
|
||||
formData.annualReport = currentYear;
|
||||
}
|
||||
},
|
||||
{ immediate: true, deep: true }
|
||||
);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="task-management">
|
||||
<div class="task-header">
|
||||
<el-tabs v-model="activeTab" @tab-change="handleTabChange">
|
||||
<el-tab-pane :label="t('workbench.task.pendingReview') + ` (${pendingCount})`" name="pending"
|
||||
<el-tab-pane :label="t('workbench.task.pendingReview') + ` (${pendingTotal})`" name="pending"
|
||||
class="task-tab-pane">
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="t('workbench.task.saved')" name="saved"></el-tab-pane>
|
||||
@@ -46,6 +46,7 @@ const emit = defineEmits<{
|
||||
const activeTab = ref('pending');
|
||||
const tableData = ref<any[]>([]);
|
||||
const loading = ref(false);
|
||||
const pendingTotal = ref(0);
|
||||
/**
|
||||
* 根据任务名称判断类型
|
||||
* @param name 任务名称
|
||||
@@ -105,18 +106,18 @@ const handleRowClick = (row: any) => {
|
||||
},
|
||||
});
|
||||
};
|
||||
const getQueryMineTask = () =>{
|
||||
return new Promise((resolve, reject) =>{
|
||||
const getQueryMineTask = (): Promise<any> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
queryMineTask({
|
||||
title: '',
|
||||
processName: '',
|
||||
taskTime: undefined,
|
||||
flowType: '',
|
||||
size:5,
|
||||
size: 5,
|
||||
current: 1
|
||||
}).then(res=>{
|
||||
}).then(res => {
|
||||
resolve(res)
|
||||
}).catch(err=>{
|
||||
}).catch(err => {
|
||||
reject(err)
|
||||
})
|
||||
})
|
||||
@@ -142,8 +143,9 @@ const loadData = async (tab: string) => {
|
||||
let res;
|
||||
loading.value = true;
|
||||
if (tab === 'pending') {
|
||||
const response = await getQueryMineTask()
|
||||
const response = await getQueryMineTask()
|
||||
res = response?.data?.records
|
||||
pendingTotal.value = response?.data?.total || 0
|
||||
}
|
||||
if (tab === 'myInitiated') {
|
||||
const response = await getQueryMineStarted()
|
||||
|
||||
@@ -415,6 +415,6 @@ export const yesOrNo:Enums[] = [
|
||||
* 级别
|
||||
* */
|
||||
export const level:Enums[] = [
|
||||
{label:'一级',value:'1'},
|
||||
{label:'二级',value:'2'},
|
||||
{label:'一级',value:'national'},
|
||||
{label:'二级',value:'provincial'},
|
||||
]
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="layout-navbars-breadcrumb-user pr15" :style="{ flex: layoutUserFlexNum }">
|
||||
<el-dropdown :show-timeout="70" :hide-timeout="50" trigger="click" @command="onLanguageChange">
|
||||
<!-- <el-dropdown :show-timeout="70" :hide-timeout="50" trigger="click" @command="onLanguageChange">
|
||||
<div class="layout-navbars-breadcrumb-user-icon">
|
||||
<i class="iconfont" :class="state.disabledI18n === 'en' ? 'icon-fuhao-yingwen' : 'icon-fuhao-zhongwen'" :title="$t('user.title1')"></i>
|
||||
</div>
|
||||
@@ -10,7 +10,7 @@
|
||||
<el-dropdown-item command="en" :disabled="state.disabledI18n === 'en'">English</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</el-dropdown> -->
|
||||
<div class="layout-navbars-breadcrumb-user-icon" @click="onLockClick">
|
||||
<el-icon :title="$t('layout.threeLockScreenTime')">
|
||||
<ele-Lock />
|
||||
|
||||
@@ -255,11 +255,11 @@ export function verifyPasswordPowerful(val: string) {
|
||||
export function verifyPasswordStrength(val: string) {
|
||||
let v = '0';
|
||||
// 弱:纯数字,纯字母,纯特殊字符
|
||||
if (/^(?:\d+|[a-zA-Z]+|[!@#$%^&\.*]+){6,100}$/.test(val)) v = '1';
|
||||
if (/^(?:\d+|[a-zA-Z]+|[!@#$%^&\.\*_]+){6,100}$/.test(val)) v = '1';
|
||||
// 中:字母+数字,字母+特殊字符,数字+特殊字符
|
||||
if (/^(?![a-zA-z]+$)(?!\d+$)(?![!@#$%^&\.*]+$)[a-zA-Z\d!@#$%^&\.*]{6,100}$/.test(val)) v = '2';
|
||||
if (/^(?![a-zA-z]+$)(?!\d+$)(?![!@#$%^&\.\*_]+$)[a-zA-Z\d!@#$%^&\.\*_]{6,100}$/.test(val)) v = '2';
|
||||
// 强:字母+数字+特殊字符
|
||||
if (/^(?![a-zA-z]+$)(?!\d+$)(?![!@#$%^&\.*]+$)(?![a-zA-z\d]+$)(?![a-zA-z!@#$%^&\.*]+$)(?![\d!@#$%^&\.*]+$)[a-zA-Z\d!@#$%^&\.*]{6,100}$/.test(val))
|
||||
if (/^(?![a-zA-z]+$)(?!\d+$)(?![!@#$%^&\.\*_]+$)(?![a-zA-z\d]+$)(?![a-zA-z!@#$%^&\.\*_]+$)(?![\d!@#$%^&\.\*_]+$)[a-zA-Z\d!@#$%^&\.\*_]{6,100}$/.test(val))
|
||||
v = '3';
|
||||
// 返回结果
|
||||
return v;
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<!-- <el-row>
|
||||
<div class="mb8" style="width: 100%">
|
||||
<el-button v-auth="'sys_user_add'" icon="folder-add" type="primary" @click="userDialogRef.openDialog()">
|
||||
{{ $t('common.addBtn') }}
|
||||
@@ -68,7 +68,7 @@
|
||||
style="float: right"
|
||||
/>
|
||||
</div>
|
||||
</el-row>
|
||||
</el-row> -->
|
||||
<el-table
|
||||
v-loading="state.loading"
|
||||
:data="state.dataList"
|
||||
@@ -111,7 +111,7 @@
|
||||
{{ $t('common.editBtn') }}
|
||||
</el-button>
|
||||
<!-- 删除用户 -->
|
||||
<el-tooltip :content="$t('sysuser.deleteDisabledTip')" :disabled="scope.row.userId !== '1'" placement="top">
|
||||
<!-- <el-tooltip :content="$t('sysuser.deleteDisabledTip')" :disabled="scope.row.userId !== '1'" placement="top">
|
||||
<span style="margin-left: 12px">
|
||||
<el-button
|
||||
icon="delete"
|
||||
@@ -123,7 +123,7 @@
|
||||
>{{ $t('common.delBtn') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</el-tooltip>
|
||||
</el-tooltip> -->
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item label="原密码" prop="password">
|
||||
<el-input v-model="passwordFormData.password" :type="showPassword ? 'text' : 'password'" placeholder="请输入密码" clearable type="password">
|
||||
<el-input v-model="passwordFormData.password" :type="showPassword ? 'text' : 'password'" placeholder="请输入密码" clearable>
|
||||
<template #suffix>
|
||||
<i
|
||||
class="iconfont el-input__icon login-content-password"
|
||||
@@ -102,7 +102,7 @@
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="第三方账号">
|
||||
<!-- <el-tab-pane label="第三方账号">
|
||||
<template #label>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M7.864 4.243A7.5 7.5 0 0 1 19.5 10.5c0 2.92-.556 5.709-1.568 8.268M5.742 6.364A7.465 7.465 0 0 0 4.5 10.5a7.464 7.464 0 0 1-1.15 3.993m1.989 3.559A11.209 11.209 0 0 0 8.25 10.5a3.75 3.75 0 1 1 7.5 0c0 .527-.021 1.049-.064 1.565M12 10.5a14.94 14.94 0 0 1-3.6 9.75m6.633-4.596a18.666 18.666 0 0 1-2.485 5.33" />
|
||||
@@ -126,7 +126,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
</el-tab-pane> -->
|
||||
</el-tabs>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
@@ -150,12 +150,12 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column v-if="isUpdate" prop="expertStatus" min-width="120" :label="t('expertApply.table.expertStatus')">
|
||||
<el-table-column v-if="isUpdate" prop="externalStatus" min-width="120" :label="t('expertApply.table.externalStatus')">
|
||||
<template #default="scope">
|
||||
<el-select v-model="scope.row.expertStatus" :placeholder="t('expertApply.table.selectPlaceholder')"
|
||||
<el-select v-model="scope.row.externalStatus" :placeholder="t('expertApply.table.selectPlaceholder')"
|
||||
style="width: 100%;">
|
||||
<el-option :label="t('expertApply.expertStatusOptions.normal')" value="normal" />
|
||||
<el-option :label="t('expertApply.expertStatusOptions.invalid')" value="invalid" />
|
||||
<el-option :label="t('expertApply.externalStatusOptions.normal')" value="normal" />
|
||||
<el-option :label="t('expertApply.externalStatusOptions.invalid')" value="invalid" />
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -230,8 +230,7 @@ const createEmptyExpert = () => ({
|
||||
attachments: '',
|
||||
evidences: '',
|
||||
level: '',
|
||||
expertStatus: 'normal',
|
||||
externalStatus: '',
|
||||
externalStatus: 'normal',
|
||||
attachmentUrl: '',
|
||||
testimonyMaterials: '',
|
||||
createBy: '',
|
||||
@@ -241,8 +240,8 @@ const createEmptyExpert = () => ({
|
||||
delFlag: '',
|
||||
processInstanceId: '',
|
||||
status: 1,
|
||||
flowType:0,
|
||||
templateId:0,
|
||||
flowType:0,
|
||||
templateId:0,
|
||||
});
|
||||
|
||||
const expertForm = reactive({
|
||||
@@ -267,7 +266,7 @@ const onExpertSelected = (row: any) => {
|
||||
target.professionalTitle = row?.professionalTitle ?? target.professionalTitle;
|
||||
target.workUnit = row?.workUnit ?? target.workUnit;
|
||||
target.level = row?.level ?? target.level;
|
||||
target.expertStatus = row?.expertStatus ?? target.expertStatus;
|
||||
target.externalStatus = row?.externalStatus ?? target.externalStatus;
|
||||
target.attachments = row?.attachmentUrl ? JSON.parse(row.attachmentUrl) : []
|
||||
target.evidences = row?.testimonyMaterials ? JSON.parse(row.testimonyMaterials) : []
|
||||
};
|
||||
@@ -312,8 +311,7 @@ const toSubmitExpert = (expert: ReturnType<typeof createEmptyExpert>) => ({
|
||||
attachmentUrl: JSON.stringify(expert.attachments),
|
||||
testimonyMaterials: JSON.stringify(expert.evidences),
|
||||
level: expert.level || '',
|
||||
expertStatus: expert.expertStatus || 'normal',
|
||||
externalStatus: expert.externalStatus || '',
|
||||
externalStatus: expert.externalStatus || 'normal',
|
||||
createBy: expert.createBy || '',
|
||||
createTime: expert.createTime || '',
|
||||
updateBy: expert.updateBy || '',
|
||||
@@ -460,7 +458,7 @@ watch(()=>tempId.value,()=>{
|
||||
professionalTitle: item.professionalTitle,
|
||||
workUnit: item.workUnit,
|
||||
level: item.level,
|
||||
expertStatus: item.expertStatus || 'normal',
|
||||
externalStatus: item.externalStatus || 'normal',
|
||||
attachments: item.attachmentUrl ? JSON.parse(item.attachmentUrl) : [],
|
||||
evidences: item.testimonyMaterials ? JSON.parse(item.testimonyMaterials) : []
|
||||
}
|
||||
|
||||
@@ -29,10 +29,10 @@
|
||||
<span>{{ levelLabel(row.level) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('expertLibrary.table.status')" prop="externalStatus" min-width="120"
|
||||
<el-table-column :label="t('expertApply.table.externalStatus')" prop="externalStatus" min-width="120"
|
||||
show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="['info', 'primary', 'success', 'danger'][row.status]">{{ externalStatusLabel(row.status) }}</el-tag>
|
||||
<el-tag :type="row.externalStatus === 'normal' ? 'success' : 'danger'">{{ externalStatusLabel(row.externalStatus) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('expertLibrary.table.action')" width="100" fixed="right">
|
||||
@@ -107,19 +107,15 @@ const handleView = (row: any) => {
|
||||
};
|
||||
|
||||
const levelLabel = (val: string) => {
|
||||
if (val === 'national') return t('expertApply.level.national');
|
||||
if (val === 'provincial') return t('expertApply.level.provincial');
|
||||
if (val === 'city') return t('expertApply.level.city');
|
||||
if (val === 'national') return '一级';
|
||||
if (val === 'provincial') return '二级';
|
||||
return val || '-';
|
||||
};
|
||||
|
||||
const externalStatusLabel = (val: string) => {
|
||||
return {
|
||||
'0': t('expertLibrary.status.pending'),
|
||||
'1': t('expertLibrary.status.reviewing'),
|
||||
'2': t('expertLibrary.status.approved'),
|
||||
'3': t('expertLibrary.status.rejected'),
|
||||
}[val] || 1
|
||||
if (val === 'normal') return t('expertApply.externalStatusOptions.normal');
|
||||
if (val === 'invalid') return t('expertApply.externalStatusOptions.invalid');
|
||||
return val || '-';
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
@@ -109,11 +109,11 @@ export default {
|
||||
attachments: 'Attachments',
|
||||
evidences: 'Supporting Materials',
|
||||
level: 'Level',
|
||||
expertStatus: 'Expert Status',
|
||||
externalStatus: 'Expert Status',
|
||||
inputPlaceholder: 'Please enter',
|
||||
selectPlaceholder: 'Please select',
|
||||
},
|
||||
expertStatusOptions: {
|
||||
externalStatusOptions: {
|
||||
normal: 'Normal',
|
||||
invalid: 'Invalid',
|
||||
},
|
||||
|
||||
@@ -109,11 +109,11 @@ export default {
|
||||
attachments: '附件',
|
||||
evidences: '佐证材料',
|
||||
level: '级别',
|
||||
expertStatus: '专家状态',
|
||||
externalStatus: '专家状态',
|
||||
inputPlaceholder: '请输入',
|
||||
selectPlaceholder: '请选择',
|
||||
},
|
||||
expertStatusOptions: {
|
||||
externalStatusOptions: {
|
||||
normal: '正常',
|
||||
invalid: '作废',
|
||||
},
|
||||
|
||||
@@ -124,7 +124,19 @@
|
||||
<span class="detail-title">{{ t('reserveLibrary.detail.title') }}</span>
|
||||
<el-button class="close-btn" link icon="Close" @click="detailDrawerVisible = false" />
|
||||
</div>
|
||||
<div class="detail-content">
|
||||
<!-- Tab 锚点导航 -->
|
||||
<div class="detail-tabs">
|
||||
<div
|
||||
v-for="tab in detailTabs"
|
||||
:key="tab.id"
|
||||
class="detail-tab-item"
|
||||
:class="{ active: activeTab === tab.id }"
|
||||
@click="scrollToSection(tab.id)"
|
||||
>
|
||||
{{ tab.label }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail-content" ref="detailContentRef" @scroll="handleDetailScroll">
|
||||
<ProjectBasicInfoView :model-value="detailFormData" :main-title="t('reserveLibrary.detail.formTitle')" />
|
||||
</div>
|
||||
</el-drawer>
|
||||
@@ -235,6 +247,59 @@ const detailFormData = reactive<ProjectBasicInfoFormData>(createEmptyDetail());
|
||||
const tableLoading = ref(false);
|
||||
const detailLoading = ref(false);
|
||||
|
||||
// Tab 锚点导航相关
|
||||
const detailContentRef = ref<HTMLElement | null>(null);
|
||||
const activeTab = ref('basicInfo');
|
||||
const detailTabs = [
|
||||
{ id: 'basicInfo', label: t('reserveRegistration.basicInfo.title') || '项目基本信息' },
|
||||
{ id: 'investmentEstimate', label: t('reserveRegistration.investmentEstimate.title') || '投资估算' },
|
||||
{ id: 'partnerInfo', label: t('reserveRegistration.partnerInfo.title') || '合作方信息' },
|
||||
{ id: 'decisionInfo', label: t('reserveRegistration.decisionInfo.title') || '决策信息' },
|
||||
];
|
||||
|
||||
const scrollToSection = (sectionId: string) => {
|
||||
activeTab.value = sectionId;
|
||||
const contentEl = detailContentRef.value;
|
||||
if (!contentEl) return;
|
||||
|
||||
// 根据 sectionId 查找对应的标题元素
|
||||
const sectionMap: Record<string, number> = {
|
||||
'basicInfo': 0,
|
||||
'investmentEstimate': 1,
|
||||
'partnerInfo': 2,
|
||||
'decisionInfo': 3,
|
||||
};
|
||||
|
||||
const sectionTitles = contentEl.querySelectorAll('.form-section-title, .sub-section-title');
|
||||
const targetIndex = sectionMap[sectionId];
|
||||
|
||||
if (sectionTitles[targetIndex]) {
|
||||
sectionTitles[targetIndex].scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
}
|
||||
};
|
||||
|
||||
const handleDetailScroll = () => {
|
||||
const contentEl = detailContentRef.value;
|
||||
if (!contentEl) return;
|
||||
|
||||
const sectionTitles = contentEl.querySelectorAll('.form-section-title, .sub-section-title');
|
||||
const scrollTop = contentEl.scrollTop;
|
||||
const offsetTop = 100; // 偏移量
|
||||
|
||||
let currentSection = 'basicInfo';
|
||||
const sectionIds = ['basicInfo', 'investmentEstimate', 'partnerInfo', 'decisionInfo'];
|
||||
|
||||
sectionTitles.forEach((el, index) => {
|
||||
const rect = el.getBoundingClientRect();
|
||||
const containerRect = contentEl.getBoundingClientRect();
|
||||
if (rect.top - containerRect.top <= offsetTop) {
|
||||
currentSection = sectionIds[index] || currentSection;
|
||||
}
|
||||
});
|
||||
|
||||
activeTab.value = currentSection;
|
||||
};
|
||||
|
||||
const resetDetailForm = () => {
|
||||
const empty = createEmptyDetail();
|
||||
Object.assign(detailFormData, empty);
|
||||
@@ -453,10 +518,39 @@ onMounted(() => {
|
||||
color: var(--el-text-color-primary);
|
||||
}
|
||||
|
||||
.detail-tabs {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0;
|
||||
padding: 0 20px;
|
||||
border-bottom: 1px solid var(--el-border-color);
|
||||
background: var(--el-bg-color);
|
||||
}
|
||||
|
||||
.detail-tab-item {
|
||||
padding: 12px 20px;
|
||||
font-size: 14px;
|
||||
color: var(--el-text-color-regular);
|
||||
cursor: pointer;
|
||||
border-bottom: 2px solid transparent;
|
||||
transition: all 0.2s ease;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.detail-tab-item:hover {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.detail-tab-item.active {
|
||||
color: var(--el-color-primary);
|
||||
border-bottom-color: var(--el-color-primary);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.detail-content {
|
||||
padding: 0 20px 20px;
|
||||
overflow: auto;
|
||||
height: calc(100% - 52px);
|
||||
height: calc(100% - 96px);
|
||||
}
|
||||
|
||||
.el-form-item--default {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="fixed top-0 right-0 z-10 flex items-center p-5 space-x-2">
|
||||
<!-- 语言切换 -->
|
||||
<el-dropdown v-if="isI18nEnabled" trigger="click" @command="onLanguageChange">
|
||||
<!-- <el-dropdown v-if="isI18nEnabled" trigger="click" @command="onLanguageChange">
|
||||
<div
|
||||
class="flex items-center justify-center transition-colors rounded-lg cursor-pointer w-9 h-9 bg-white/80 dark:bg-slate-800/80 hover:bg-gray-100 dark:hover:bg-slate-700 backdrop-blur-sm"
|
||||
>
|
||||
@@ -16,7 +16,7 @@
|
||||
<el-dropdown-item command="en" :disabled="state.disabledI18n === 'en'">English</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</el-dropdown> -->
|
||||
|
||||
<!-- 主题切换 -->
|
||||
<el-tooltip v-if="isDarkModeEnabled" :content="getThemeConfig.isDark ? '切换亮色模式' : '切换暗色模式'" placement="bottom">
|
||||
|
||||
@@ -208,6 +208,7 @@ export default {
|
||||
startTime: 'Initiation Time',
|
||||
receivedTime: 'Received Time',
|
||||
processTime: 'Process Time',
|
||||
currentNode: 'Current Node',
|
||||
status: 'Approval Status',
|
||||
actions: 'Actions',
|
||||
},
|
||||
|
||||
@@ -208,6 +208,7 @@ export default {
|
||||
startTime: '发起时间',
|
||||
receivedTime: '接收时间',
|
||||
processTime: '处理时间',
|
||||
currentNode: '当前节点',
|
||||
status: '审批状态',
|
||||
actions: '操作',
|
||||
},
|
||||
|
||||
@@ -55,6 +55,11 @@
|
||||
min-width="180" />
|
||||
<el-table-column prop="updateTime" :label="t('workbenchPage.pending.table.processTime')"
|
||||
min-width="180" />
|
||||
<el-table-column prop="taskName" :label="t('workbenchPage.pending.table.currentNode')" min-width="140">
|
||||
<template #default="scope">
|
||||
{{ scope.row.taskName || scope.row.nodeName || scope.row.currentNode || '--' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" :label="t('workbenchPage.pending.table.status')" min-width="140">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.status == 1" type="primary">待审批</el-tag>
|
||||
|
||||
Reference in New Issue
Block a user