diff --git a/rebuild.sh b/rebuild.sh
index 16b2131..d404ab9 100644
--- a/rebuild.sh
+++ b/rebuild.sh
@@ -123,3 +123,4 @@ echo ""
+
diff --git a/src/api/flow/index.js b/src/api/flow/index.js
index d0d3273..526f4e7 100644
--- a/src/api/flow/index.js
+++ b/src/api/flow/index.js
@@ -339,3 +339,23 @@ export function getRelationList(flowId, isLoading = false) {
isLoading
})
}
+
+// 打捞流程至付款流程
+export function salvageFlow(flowId, isLoading = true) {
+ return request({
+ method: 'post',
+ url: '/api/oa/flow/salvage',
+ data: { flow_id: flowId },
+ isLoading
+ })
+}
+
+// 取消打捞
+export function cancelSalvage(flowId, isLoading = true) {
+ return request({
+ method: 'post',
+ url: '/api/oa/flow/cancel-salvage',
+ data: { flow_id: flowId },
+ isLoading
+ })
+}
diff --git a/src/components/BudgetSourcePickerField.vue b/src/components/BudgetSourcePickerField.vue
index 87e9fab..6f52f7a 100644
--- a/src/components/BudgetSourcePickerField.vue
+++ b/src/components/BudgetSourcePickerField.vue
@@ -219,8 +219,49 @@
+
+
抵消预付账款
+
+
+ {{ node.label }}
+
+
+ 预算:
+ {{ formatAmount(data.budget_amount) }}
+
+
+ 已用:
+ {{ formatAmount(data.used_amount || 0) }}
+
+
+ 执行率:
+
+ {{ formatExecutionRate(data.execution_rate || 0) }}
+
+
+
+
+
+
+
暂无数据
@@ -462,12 +503,16 @@ export default {
lastYearCarryoverTree() {
return (this.treeData || []).filter((n) => n?.budget_type === "last_year_carryover");
},
+ offsetPrepaidTree() {
+ return (this.treeData || []).filter((n) => n?.budget_type === "offset_prepaid");
+ },
budgetTypeMap() {
return {
department: "部门预算",
project: "自有账户",
special_fund: "专项资金",
last_year_carryover: "上一年结转资金",
+ offset_prepaid: "抵消预付账款",
};
},
// 分配总额
@@ -624,6 +669,7 @@ export default {
this.$refs.projectTreeRef,
this.$refs.specialFundTreeRef,
this.$refs.lastYearCarryoverTreeRef,
+ this.$refs.offsetPrepaidTreeRef,
].filter(Boolean);
const nodes = [];
refs.forEach((tree) => {
@@ -646,6 +692,7 @@ export default {
this.$refs.projectTreeRef,
this.$refs.specialFundTreeRef,
this.$refs.lastYearCarryoverTreeRef,
+ this.$refs.offsetPrepaidTreeRef,
].filter(Boolean);
refs.forEach((tree) => {
if (typeof tree.setCheckedKeys === "function") {
diff --git a/src/components/ContractSignField.vue b/src/components/ContractSignField.vue
index f719d9f..bb70f9d 100644
--- a/src/components/ContractSignField.vue
+++ b/src/components/ContractSignField.vue
@@ -96,7 +96,7 @@
-
+
-
+
-
+
-
+
@@ -328,7 +328,6 @@ import {
getContractByFlowId,
createContract,
getBudgetYearOptions,
- save,
} from "@/api/flow";
import { userListNoAuth, departmentListNoAuth } from "@/api/common";
@@ -439,17 +438,17 @@ export default {
// 监听多选下拉变化,触发校验
'form.owner_department_ids_array'() {
if (this.$refs.formRef) {
- this.$refs.formRef.validateField('owner_department_ids', () => {});
+ this.$refs.formRef.validateField('owner_department_ids_array', () => {});
}
},
'form.handler_admin_ids_array'() {
if (this.$refs.formRef) {
- this.$refs.formRef.validateField('handler_admin_ids', () => {});
+ this.$refs.formRef.validateField('handler_admin_ids_array', () => {});
}
},
'form.apply_handler_id_array'() {
if (this.$refs.formRef) {
- this.$refs.formRef.validateField('apply_handler_id', () => {});
+ this.$refs.formRef.validateField('apply_handler_id_array', () => {});
}
},
'form.pay_plans'() {
@@ -463,6 +462,16 @@ export default {
this.$refs.formRef.validateField('amount_description', () => {});
}
},
+ // 监听合同类型变化,动态调整预算年度和项目经费来源的必填校验
+ 'form.contract_type_id'() {
+ // 当合同类型改变时,重新验证相关字段
+ if (this.$refs.formRef) {
+ this.$nextTick(() => {
+ this.$refs.formRef.validateField('fund_source_year_id');
+ this.$refs.formRef.validateField('fund_source_budget_data_id');
+ });
+ }
+ },
},
async mounted() {
// 组件初始化时,如果value为空但有flowId,尝试从流程数据恢复合同
@@ -514,7 +523,21 @@ export default {
trigger: 'blur'
}
],
- budget_amount: [{ required: true, message: "请填写项目预算金额", trigger: "blur" }],
+ budget_amount: [
+ { required: true, message: "请填写项目预算金额", trigger: "blur" },
+ {
+ validator: (rule, value, callback) => {
+ if (value === null || value === undefined || value === '') {
+ callback(new Error('请填写项目预算金额'));
+ } else if (value === 0) {
+ callback(new Error('项目预算金额不能为0'));
+ } else {
+ callback();
+ }
+ },
+ trigger: 'blur'
+ }
+ ],
contract_type_id: [{ required: true, message: "请选择合同类型", trigger: "change" }],
sign_date: [{ required: true, message: "请选择签订日期", trigger: "change" }],
apply_date: [{ required: true, message: "请选择申请日期", trigger: "change" }],
@@ -531,18 +554,56 @@ export default {
// 编辑时全量校验(与合同编辑页面对齐)
this.rules = {
...baseRules,
- fund_source_year_id: [{ required: true, message: "请选择预算年度", trigger: "change" }],
- fund_source_budget_data_id: [{ required: true, message: "请选择项目经费来源", trigger: "change" }],
+ fund_source_year_id: [
+ {
+ validator: (rule, value, callback) => {
+ // 默认必填,只有当合同类型明确是"收入类"时才撤销必填校验
+ const contractTypeName = this.getContractTypeName();
+ if (contractTypeName === '收入类') {
+ // 收入类不需要必填
+ callback();
+ } else {
+ // 其他情况(包括未选择合同类型)都需要必填
+ if (!value) {
+ callback(new Error('请选择预算年度'));
+ } else {
+ callback();
+ }
+ }
+ },
+ trigger: 'change'
+ }
+ ],
+ fund_source_budget_data_id: [
+ {
+ validator: (rule, value, callback) => {
+ // 默认必填,只有当合同类型明确是"收入类"时才撤销必填校验
+ const contractTypeName = this.getContractTypeName();
+ if (contractTypeName === '收入类') {
+ // 收入类不需要必填
+ callback();
+ } else {
+ // 其他情况(包括未选择合同类型)都需要必填
+ if (!value) {
+ callback(new Error('请选择项目经费来源'));
+ } else {
+ callback();
+ }
+ }
+ },
+ trigger: 'change'
+ }
+ ],
is_government_purchase: [{ required: true, message: "请选择是否为政府采购", trigger: "change" }],
purchase_category_id: [{ required: true, message: "请选择采购类别", trigger: "change" }],
purchase_method_id: [{ required: true, message: "请选择采购方式", trigger: "change" }],
is_accepted: [{ required: true, message: "请选择是否验收", trigger: "change" }],
- handler_admin_ids: [
+ handler_admin_ids_array: [
{
required: true,
validator: (rule, value, callback) => {
- const ids = this.form.handler_admin_ids_array;
- if (!ids || !Array.isArray(ids) || ids.length === 0) {
+ const ids = Array.isArray(value) ? value : [];
+ if (ids.length === 0) {
callback(new Error('请选择合同签订/变更经办人'));
} else {
callback();
@@ -551,12 +612,12 @@ export default {
trigger: ['change', 'blur']
}
],
- apply_handler_id: [
+ apply_handler_id_array: [
{
required: true,
validator: (rule, value, callback) => {
- const ids = this.form.apply_handler_id_array;
- if (!ids || !Array.isArray(ids) || ids.length === 0) {
+ const ids = Array.isArray(value) ? value : [];
+ if (ids.length === 0) {
callback(new Error('请选择申请科室经办人'));
} else {
callback();
@@ -565,12 +626,12 @@ export default {
trigger: ['change', 'blur']
}
],
- owner_department_ids: [
+ owner_department_ids_array: [
{
required: true,
validator: (rule, value, callback) => {
- const ids = this.form.owner_department_ids_array;
- if (!ids || !Array.isArray(ids) || ids.length === 0) {
+ const ids = Array.isArray(value) ? value : [];
+ if (ids.length === 0) {
callback(new Error('请选择科室'));
} else {
callback();
@@ -614,12 +675,30 @@ export default {
]
};
},
+ // 获取合同类型名称
+ getContractTypeName() {
+ if (!this.form.contract_type_id) {
+ return null;
+ }
+ const contractType = this.contractTypeOptions.find(type => type.id === this.form.contract_type_id);
+ return contractType ? contractType.name : null;
+ },
handleFundSourceYearChange() {
this.form.fund_source_budget_data_id = null;
if (this.form.fund_source_year_id) {
this.fetchFundSourceOptions(this.form.fund_source_year_id);
}
},
+ // 处理合同类型变化
+ handleContractTypeChange() {
+ // 当合同类型改变时,重新验证预算年度和项目经费来源
+ if (this.$refs.formRef) {
+ this.$nextTick(() => {
+ this.$refs.formRef.validateField('fund_source_year_id');
+ this.$refs.formRef.validateField('fund_source_budget_data_id');
+ });
+ }
+ },
async fetchBudgetYearOptions() {
try {
const response = await getBudgetYearOptions(false);
@@ -829,7 +908,41 @@ export default {
// 再次清除校验状态,确保填充数据后不会触发校验
this.$nextTick(() => {
if (this.$refs.formRef) {
- this.$refs.formRef.clearValidate();
+ // 如果是编辑模式,在数据加载完成后,需要正确设置多选字段的校验状态
+ if (this.isEditMode()) {
+ // 延迟一下,确保DOM已更新和数据已填充
+ this.$nextTick(() => {
+ if (this.$refs.formRef) {
+ // 对于多选字段,如果数组有值,清除校验错误;如果为空,触发校验显示错误
+ // 这样确保编辑模式下,已填充的字段不显示错误,未填充的字段显示错误
+ const fieldsToCheck = [
+ { prop: 'owner_department_ids_array', arrayField: 'owner_department_ids_array' },
+ { prop: 'handler_admin_ids_array', arrayField: 'handler_admin_ids_array' },
+ { prop: 'apply_handler_id_array', arrayField: 'apply_handler_id_array' },
+ ];
+
+ fieldsToCheck.forEach(({ prop, arrayField }) => {
+ const ids = this.form[arrayField];
+ if (ids && Array.isArray(ids) && ids.length > 0) {
+ // 有值,清除该字段的校验错误
+ this.$refs.formRef.clearValidate(prop);
+ } else {
+ // 无值,先清除之前的错误状态,然后触发校验显示错误
+ // 这样可以确保校验状态正确更新
+ this.$refs.formRef.clearValidate(prop);
+ this.$nextTick(() => {
+ if (this.$refs.formRef) {
+ this.$refs.formRef.validateField(prop, () => {});
+ }
+ });
+ }
+ });
+ }
+ });
+ } else {
+ // 新建模式,清除所有校验状态
+ this.$refs.formRef.clearValidate();
+ }
}
});
},
@@ -1222,11 +1335,12 @@ export default {
this.dialogVisible = false;
Message.success(contractId ? "合同更新成功" : "合同创建成功");
- // 如果是新建合同(不是更新)且有flowId,自动保存到流程字段
+ // 如果是新建合同(不是更新)且有flowId,自动保存到流程字段(轻量接口)
if (!contractId && this.flowId && this.fieldName) {
try {
- await save(this.flowId, {
- [this.fieldName]: String(data.id),
+ await request.post(`/api/oa/flow/save-field/${this.flowId}`, {
+ field_name: this.fieldName,
+ field_value: String(data.id),
});
// 静默保存,不显示提示
} catch (e) {
diff --git a/src/utils/formBuilder.js b/src/utils/formBuilder.js
index 33c7b00..3f18981 100644
--- a/src/utils/formBuilder.js
+++ b/src/utils/formBuilder.js
@@ -1420,6 +1420,23 @@ export default function formBuilder(
);
break;
default:
+ // flow_title 在只读模式下,需要添加发起人和创建时间后缀
+ let textDisplayValue = target[info.name] || "";
+ if (info.name === "flow_title" && this.config && this.config.flow) {
+ // 优先从 logs[0].user.name 获取发起人,其次从 flow.creator.name
+ const firstLogCreator = this.logs && this.logs.length > 0 ? this.logs[0].user?.name : null;
+ const flowCreator = this.config.flow.creator?.name || null;
+ const creator = firstLogCreator || flowCreator || "";
+ const createdAt = this.config.flow.created_at;
+ const formattedDate = createdAt
+ ? moment(createdAt).format("YYYY-MM-DD H:mm")
+ : "";
+ // 格式化后缀:【发起人 | 创建时间】,格式:yyyy-mm-dd H:i
+ if (creator || formattedDate) {
+ const suffix = `【${creator || ""} | ${formattedDate}】`;
+ textDisplayValue = textDisplayValue + suffix;
+ }
+ }
formItem = h(
"span",
{
@@ -1427,7 +1444,7 @@ export default function formBuilder(
color: "#333",
},
},
- target[info.name]
+ textDisplayValue
);
}
}
@@ -1563,8 +1580,9 @@ export default function formBuilder(
},
isJointly
? [info._writeable ? formItem : "", jointlySignContent]
- : [
- (() => {
+ : (() => {
+ // 获取字段值显示内容
+ let fieldValueDisplay = (() => {
if (info.name === "flow_title") {
return formItem;
}
@@ -1582,95 +1600,106 @@ export default function formBuilder(
if ((log && log.status) || log?.user?.id === this.$store.state.user.adminId || this.logs.length === 0) {
return formItem;
}
+ // 如果没有符合条件的 log,也返回 formItem 以确保显示
+ return formItem;
}
- })(),
- (() => {
- // 检查 is_sign 是否为真值(支持 1, true, "1" 等)
- if (info.is_sign && (info.is_sign === 1 || info.is_sign === true || info.is_sign === "1")) {
- let log = null;
-
- // 方法1: 从 jointlySignLog 中查找(检查所有字段)
- if (this.jointlySignLog && this.jointlySignLog.length > 0) {
- log = this.jointlySignLog.find((log) => {
- if (log.status > 0 && log.user && log.user.sign_file && log.user.sign_file.url) {
- try {
- const data = JSON.parse(log.data);
- // 检查 data 中是否有匹配当前字段的条目
- return Object.values(data).some(
- (value) =>
- value &&
- typeof value === 'object' &&
- value.custom_field_id === info.id
- );
- } catch (e) {
- return false;
+ })();
+
+ // 检查是否有签名需要显示
+ let signLog = null;
+ if (info.is_sign && (info.is_sign === 1 || info.is_sign === true || info.is_sign === "1")) {
+ // 方法1: 从 jointlySignLog 中查找(检查所有字段)
+ if (this.jointlySignLog && this.jointlySignLog.length > 0) {
+ signLog = this.jointlySignLog.find((log) => {
+ if (log.status > 0 && log.user && log.user.sign_file && log.user.sign_file.url) {
+ try {
+ const data = JSON.parse(log.data);
+ // 检查 data 中是否有匹配当前字段的条目
+ return Object.values(data).some(
+ (value) =>
+ value &&
+ typeof value === 'object' &&
+ value.custom_field_id === info.id
+ );
+ } catch (e) {
+ return false;
+ }
+ }
+ return false;
+ });
+ }
+
+ // 方法2: 如果没找到,从 this.logs 中查找(必须匹配字段ID和类型)
+ if (!signLog && this.logs && this.logs.length > 0) {
+ signLog = this.logs.find(
+ (log) =>
+ log.status > 0 &&
+ log.user &&
+ log.user.sign_file &&
+ log.user.sign_file.url &&
+ log.node?.fields?.findIndex(
+ (field) =>
+ field?.field_id === info.id &&
+ field.type === "write"
+ ) !== -1
+ );
+ }
+ }
+
+ // 如果找到签名日志,重新组织显示顺序
+ if (signLog && signLog.status > 0 && signLog.user && signLog.user.sign_file && signLog.user.sign_file.url) {
+ const signDate = signLog.updated_at
+ ? this.$moment(signLog.updated_at).format("YYYY年MM月DD日")
+ : "";
+ const signImageUrl = signLog.user.sign_file.url;
+
+ return [
+ // 1. input框的内容(独占一行)
+ fieldValueDisplay,
+ // 2. 签名图片(容器占一行)
+ h("div", {
+ style: {
+ "margin-top": "8px",
+ "margin-bottom": "8px",
+ }
+ }, [
+ h("img", {
+ style: {
+ display: "block",
+ "max-height": "80px",
+ "max-width": "100px",
+ cursor: "pointer",
+ },
+ attrs: {
+ src: signImageUrl,
+ alt: signLog.user?.name || "",
+ },
+ on: {
+ click: () => {
+ this.$bus.$emit("online-file", signImageUrl);
}
}
- return false;
- });
- }
-
- // 方法2: 如果没找到,从 this.logs 中查找(必须匹配字段ID和类型)
- if (!log && this.logs && this.logs.length > 0) {
- log = this.logs.find(
- (log) =>
- log.status > 0 &&
- log.user &&
- log.user.sign_file &&
- log.user.sign_file.url &&
- log.node?.fields?.findIndex(
- (field) =>
- field?.field_id === info.id &&
- field.type === "write"
- ) !== -1
- );
- }
-
- // 只有当找到匹配当前字段的log时才显示
- if (log && log.status > 0 && log.user && log.user.sign_file && log.user.sign_file.url) {
- return h("div", {
+ })
+ ]),
+ // 3. 日期(独占一行)
+ h("div", {
+ style: {
+ "margin-top": "4px",
+ }
+ }, [
+ h("span", {
style: {
- "margin-top": "8px",
- display: "flex",
- "align-items": "center",
- gap: "8px",
+ "font-size": "16px",
+ color: "#000",
}
- }, [
- h("el-image", {
- style: {
- "max-height": "80px",
- "max-width": "100px",
- display: "block",
- },
- props: {
- src: log.user.sign_file.url,
- fit: "contain",
- alt: log.user?.name || "",
- "preview-src-list": [log.user.sign_file.url],
- lazy: false,
- },
- attrs: {
- src: log.user.sign_file.url,
- },
- }),
- h(
- "div",
- {
- style: {
- "font-size": "16px",
- color: "#000",
- }
- },
- log.updated_at
- ? this.$moment(log.updated_at).format("YYYY年MM月DD日")
- : ""
- ),
- ]);
- }
- }
- return null;
- })(),
- ]
+ }, signDate)
+ ])
+ ];
+ } else {
+ // 没有签名时,只显示字段值
+ return [fieldValueDisplay];
+ }
+ })()
);
}
}
diff --git a/src/views/flow/DesktopForm.vue b/src/views/flow/DesktopForm.vue
index 472e215..f03f246 100644
--- a/src/views/flow/DesktopForm.vue
+++ b/src/views/flow/DesktopForm.vue
@@ -440,8 +440,6 @@ export default {
padding: 8px 12px;
margin: -1px 0 0 -1px;
}
-::v-deep .el-form-item__content {
-}
::v-deep .el-radio, .el-radio__input {
line-height: 1.5;
}
diff --git a/src/views/flow/create.vue b/src/views/flow/create.vue
index 0e94cfe..09674a7 100644
--- a/src/views/flow/create.vue
+++ b/src/views/flow/create.vue
@@ -281,10 +281,26 @@
-
+
+ {{ fi.name || '附件' }}
+
+
{{ fi.name || '附件' }}
- {{ fi.name || '附件' }}
-
@@ -929,12 +945,38 @@ export default {
return (field && (field.key || field.field_key)) ? (field.key || field.field_key) : '';
},
- // 相关事前流程字段是否渲染:目前仅对 oa_custom_model/out_custom_model 做“空值隐藏”
+ // 相关事前流程字段是否渲染:
+ // 1. 最顶层拦截:如果画布配置中 visible === false,则不显示
+ // 2. 对 oa_custom_model/out_custom_model 做"空值隐藏"
+ // 3. 对 meeting_minutes 和 attachment/file 做"空值隐藏"
shouldRenderPlannedField(expenditureId, field) {
if (!field) return false;
+
+ // 最顶层拦截:如果画布配置中关闭了元素的显示(visible === false),则不显示
+ if (field.visible === false) {
+ return false;
+ }
+
+ // oa_custom_model/out_custom_model 类型:仅当有绑定值时才显示
if (field.element_type === 'oa_custom_model' || field.element_type === 'out_custom_model') {
return this.getOaCustomModelBindings(expenditureId, field).length > 0;
}
+
+ // meeting_minutes 类型:仅当有会议纪要ID时才显示
+ if (field.element_type === 'meeting_minutes') {
+ const meetingMinuteId = this.extractMeetingMinuteId(
+ this.getIndirectFieldValue(expenditureId, this.getTplFieldKey(field))
+ );
+ return meetingMinuteId !== null && meetingMinuteId !== undefined;
+ }
+
+ // attachment/file 类型:仅当有文件项时才显示
+ if (field.element_type === 'attachment' || field.element_type === 'file') {
+ const fileItems = this.getPlannedFileItems(expenditureId, field);
+ return fileItems && fileItems.length > 0;
+ }
+
+ // 其他类型:默认显示(已通过 visible 检查)
return true;
},
@@ -1692,6 +1734,47 @@ export default {
},
// ---------- 事前流程附件/文件 ----------
+ // 构建完整的文件URL(如果是相对路径,自动拼接当前域名)
+ buildFileUrl(path) {
+ if (!path) return null;
+
+ const pathStr = String(path).trim();
+ if (!pathStr) return null;
+
+ // 如果已经是完整URL(http:// 或 https://),直接返回
+ if (pathStr.startsWith('http://') || pathStr.startsWith('https://')) {
+ return pathStr;
+ }
+
+ // 如果是相对路径(以 / 开头),拼接当前域名
+ if (pathStr.startsWith('/')) {
+ const origin = window.location.origin;
+ return `${origin}${pathStr}`;
+ }
+
+ // 其他情况,尝试拼接(可能是 storage/files/xxx 这种格式)
+ if (pathStr.includes('/')) {
+ const origin = window.location.origin;
+ return `${origin}/${pathStr.replace(/^\//, '')}`;
+ }
+
+ // 无法处理的格式,返回原始值
+ return pathStr;
+ },
+
+ // 处理附件点击
+ handlePlannedFileClick(file) {
+ if (file.url) {
+ try {
+ window.open(file.url, '_blank');
+ } catch (error) {
+ this.$message.error('打开文件失败:' + error.message);
+ }
+ } else {
+ this.$message.warning('文件链接不可用');
+ }
+ },
+
getPlannedFileItems(expenditureId, field) {
const v = this.getIndirectFieldValue(expenditureId, field?.key);
if (!v) return [];
@@ -1701,19 +1784,33 @@ export default {
const s = x.trim();
if (!s) return null;
const name = s.split('/').pop();
- return { name: name || `附件${idx + 1}`, url: s };
+ const url = this.buildFileUrl(s);
+ return { name: name || `附件${idx + 1}`, url };
}
if (typeof x === 'object') {
- const url = x.url || x.path || x.file_url || x.download_url || x.href || x.preview_url;
+ // 尝试从多个可能的字段中获取路径
+ let rawPath = x.url || x.path || x.file_url || x.download_url || x.href || x.preview_url || x.value || x.file_path || x.folder;
+
+ // 如果还是没有,检查对象的所有值,看是否有字符串类型的路径
+ if (!rawPath) {
+ for (const [key, value] of Object.entries(x)) {
+ if (typeof value === 'string' && (value.includes('/') || value.includes('storage') || value.includes('files'))) {
+ rawPath = value;
+ break;
+ }
+ }
+ }
+
+ const url = rawPath ? this.buildFileUrl(rawPath) : null;
const name =
x.original_name ||
x.file_name ||
x.name ||
x.filename ||
x.originalName ||
- (url ? String(url).split('/').pop() : null);
- if (url || name) return { name: name || `附件${idx + 1}`, url };
- return null;
+ (url ? String(url).split('/').pop() : null) ||
+ (rawPath ? String(rawPath).split('/').pop() : null);
+ return (url || name) ? { name: name || `附件${idx + 1}`, url } : null;
}
return null;
};
@@ -2644,7 +2741,7 @@ export default {
table-layout: fixed;
}
::v-deep .el-descriptions-item__label {
- width: 200px;
+ width: 250px;
white-space: nowrap;
}
::v-deep .el-descriptions-item__content {
diff --git a/src/views/flow/list.vue b/src/views/flow/list.vue
index 845fc32..9669e74 100644
--- a/src/views/flow/list.vue
+++ b/src/views/flow/list.vue
@@ -397,7 +397,17 @@
content:
'点击工作名称查看简要。\n批量审批请先选择流程类型,并勾选同一节点的流程。',
}"
- >
+ >
+
+ 打捞
+ {{ row.title }}
+
+
= cutoffDate) {
+ this.$message.warning("只有2026-01-01之前创建的流程可以打捞");
+ return;
+ }
+ this.handleSalvage(row);
+ break;
+ case "cancel_salvage":
+ if (!row.is_salvaged) {
+ this.$message.warning("该流程未被打捞");
+ return;
+ }
+ this.handleCancelSalvage(row);
+ break;
default:
}
},
+ async handleSalvage(row) {
+ try {
+ await this.$confirm("确认打捞该流程至付款流程?", "提示", {
+ type: "warning",
+ });
+ const res = await salvageFlow(row.id);
+ // 响应拦截器已经处理,成功时返回的是data部分(字符串消息)
+ this.$message.success(res || "打捞成功");
+ this.getList();
+ } catch (err) {
+ if (err !== "cancel") {
+ this.$message.error(err.message || err.msg || "打捞失败");
+ }
+ }
+ },
+ async handleCancelSalvage(row) {
+ try {
+ await this.$confirm("确认取消打捞该流程?", "提示", {
+ type: "warning",
+ });
+ const res = await cancelSalvage(row.id);
+ // 响应拦截器已经处理,成功时返回的是data部分(字符串消息)
+ this.$message.success(res || "取消打捞成功");
+ this.getList();
+ } catch (err) {
+ if (err !== "cancel") {
+ this.$message.error(err.message || err.msg || "取消打捞失败");
+ }
+ }
+ },
contentFormatter(row) {
const { data, fields } = row;
let text = "";
@@ -1135,11 +1197,6 @@ export default {
},
computed: {
menuConfig() {
- console.log(
- "this.$store.state",
- this.$store.state,
- this.$store.state.user.roles.includes("全局流程监管")
- );
if (this.$store.state.user.roles.includes("全局流程监管")) {
return {
className: "my-menus",
@@ -1151,6 +1208,16 @@ export default {
name: "编辑",
prefixConfig: { icon: "vxe-icon-feedback" },
},
+ {
+ code: "salvage",
+ name: "打捞至付款流程",
+ prefixConfig: { icon: "vxe-icon-check" },
+ },
+ {
+ code: "cancel_salvage",
+ name: "取消打捞",
+ prefixConfig: { icon: "vxe-icon-close" },
+ },
],
],
},
diff --git a/自定义字段渲染整理.md b/自定义字段渲染整理.md
index ec061bc..11fe5fa 100644
--- a/自定义字段渲染整理.md
+++ b/自定义字段渲染整理.md
@@ -1157,3 +1157,4 @@ form 对象存储字段值
+
diff --git a/错误诊断和解决方案.md b/错误诊断和解决方案.md
index 3a960c3..f490e82 100644
--- a/错误诊断和解决方案.md
+++ b/错误诊断和解决方案.md
@@ -194,3 +194,4 @@ A: 这是正常的,Vue CLI使用内容哈希来生成文件名。只要HTML文
+